The g? structs had a mix of variables that were written to
the savefile, and those that were not.
For better clarity and to distinguish those that end up in
the savefile, relocate some g? variables that get written
directly to the savefile into different structs.
This updates EDITLEVEL, although technically it probably
didn't need to, since savefile contents are not changing.
Details:
gb.bases -> svb.bases
gb.bbubbles -> svb.bbubbles
gb.branches -> svb.branches
gc.context -> svc.context
gd.disco -> svd.disco
gd.dndest -> svd.dndest
gd.doors -> svd.doors
gd.doors_alloc -> svd.doors_alloc
gd.dungeon_topology -> svd.dungeon_topology
gd.dungeons -> svd.dungeons
ge.exclusion_zones -> sve.exclusion_zones
gh.hackpid -> svh.hackpid
gi.inv_pos -> svi.inv_pos
gk.killer -> svk.killer
gl.lastseentyp -> svl.lastseentyp
gl.level -> svl.level
gl.level_info -> svl.level_info
gm.mapseenchn -> svm.mapseenchn
gm.moves -> svm.moves
gm.mvitals -> svm.mvitals
gn.n_dgns -> svn.n_dgns
gn.n_regions -> svn.n_regions
gn.nroom -> svn.nroom
go.oracle_cnt -> svo.oracle_cnt
gp.pl_character -> svp.pl_character
gp.pl_fruit -> svp.pl_fruit
gp.plname -> svp.plname
gp.program_state -> svp.program_state
gq.quest_status -> svq.quest_status
gr.rooms -> svr.rooms
gs.sp_levchn -> svs.sp_levchn
gs.spl_book -> svs.spl_book
gt.timer_id -> svt.timer_id
gt.tune -> svt.tune
gu.updest -> svu.updest
gx.xmax -> svx.xmax
gx.xmin -> svx.xmin
gy.ymax -> svy.ymax
gy.ymin -> svy.ymin
Related note:
There are some pointer variables that are heads of chains that were not
moved from 'g?' to 'sv?', because they are not actually written to the
savefile directly, but the objects/monst/trap/lightsource/timer in the
chains they point to are. That can be changed, if desired.
Examples: gi.invent, gm.migrating_objs, gb.billobjs, gm.migrating_mons,
gf.ftrap, gl.light_base, gt.timer_base
481 lines
9.9 KiB
C
481 lines
9.9 KiB
C
/* NetHack 3.6 macfile.c $NHDT-Date: 1432512798 2015/05/25 00:13:18 $ $NHDT-Branch: master $:$NHDT-Revision: 1.11 $ */
|
|
/* Copyright (c) Jon W{tte, Hao-Yang Wang, Jonathan Handler 1992. */
|
|
/* NetHack may be freely redistributed. See license for details. */
|
|
/*
|
|
* macfile.c
|
|
* MAC file I/O routines
|
|
*/
|
|
|
|
#include "hack.h"
|
|
#include "macwin.h"
|
|
|
|
#ifndef __MACH__
|
|
#include <files.h>
|
|
#include <errors.h>
|
|
#include <resources.h>
|
|
#include <memory.h>
|
|
#include <TextUtils.h>
|
|
#include <ToolUtils.h>
|
|
#endif
|
|
|
|
#include "dlb.h"
|
|
|
|
/*
|
|
* We should get the default dirID and volRefNum (from name) from prefs and
|
|
* the situation at startup... For now, this will have to do.
|
|
*/
|
|
|
|
/* The HandleFiles are resources built into the application which are treated
|
|
as read-only files: if we fail to open a file we look for a resource */
|
|
|
|
#define FIRST_HF 32000 /* file ID of first HandleFile */
|
|
#define MAX_HF 6 /* Max # of open HandleFiles */
|
|
|
|
#define APP_NAME_RES_ID (-16396)
|
|
|
|
typedef struct handlefile {
|
|
long type; /* Resource type */
|
|
short id; /* Resource id */
|
|
long mark; /* Current position */
|
|
long size; /* total size */
|
|
Handle data; /* The resource, purgeable */
|
|
} HandleFile;
|
|
|
|
static HandleFile *IsHandleFile(int);
|
|
static int OpenHandleFile(const unsigned char *, long);
|
|
static int CloseHandleFile(int);
|
|
static int ReadHandleFile(int, void *, unsigned);
|
|
static long SetHandleFilePos(int, short, long);
|
|
|
|
HandleFile theHandleFiles[MAX_HF];
|
|
MacDirs theDirs; /* also referenced in macwin.c */
|
|
|
|
static HandleFile *
|
|
IsHandleFile(int fd)
|
|
{
|
|
HandleFile *hfp = NULL;
|
|
|
|
if (fd >= FIRST_HF && fd < FIRST_HF + MAX_HF) {
|
|
/* in valid range, check for data */
|
|
hfp = &theHandleFiles[fd - FIRST_HF];
|
|
if (!hfp->data)
|
|
hfp = NULL;
|
|
}
|
|
return hfp;
|
|
}
|
|
|
|
static int
|
|
OpenHandleFile(const unsigned char *name, long fileType)
|
|
{
|
|
int i;
|
|
Handle h;
|
|
Str255 s;
|
|
|
|
for (i = 0; i < MAX_HF; i++) {
|
|
if (theHandleFiles[i].data == 0L)
|
|
break;
|
|
}
|
|
|
|
if (i >= MAX_HF)
|
|
return -1;
|
|
|
|
h = GetNamedResource(fileType, name);
|
|
if (!h)
|
|
return (-1);
|
|
|
|
theHandleFiles[i].data = h;
|
|
theHandleFiles[i].size = GetHandleSize(h);
|
|
GetResInfo(h, &theHandleFiles[i].id, (void *) &theHandleFiles[i].type, s);
|
|
theHandleFiles[i].mark = 0L;
|
|
|
|
return (i + FIRST_HF);
|
|
}
|
|
|
|
static int
|
|
CloseHandleFile(int fd)
|
|
{
|
|
if (!IsHandleFile(fd)) {
|
|
return -1;
|
|
}
|
|
fd -= FIRST_HF;
|
|
ReleaseResource(theHandleFiles[fd].data);
|
|
theHandleFiles[fd].data = 0L;
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
ReadHandleFile(int fd, void *ptr, unsigned len)
|
|
{
|
|
unsigned maxBytes;
|
|
Handle h;
|
|
|
|
if (!IsHandleFile(fd))
|
|
return -1;
|
|
|
|
fd -= FIRST_HF;
|
|
maxBytes = theHandleFiles[fd].size - theHandleFiles[fd].mark;
|
|
if (len > maxBytes)
|
|
len = maxBytes;
|
|
|
|
h = theHandleFiles[fd].data;
|
|
|
|
HLock(h);
|
|
BlockMove(*h + theHandleFiles[fd].mark, ptr, len);
|
|
HUnlock(h);
|
|
theHandleFiles[fd].mark += len;
|
|
|
|
return (len);
|
|
}
|
|
|
|
static long
|
|
SetHandleFilePos(int fd, short whence, long pos)
|
|
{
|
|
long curpos;
|
|
|
|
if (!IsHandleFile(fd))
|
|
return -1;
|
|
|
|
fd -= FIRST_HF;
|
|
|
|
curpos = theHandleFiles[fd].mark;
|
|
switch (whence) {
|
|
case SEEK_CUR:
|
|
curpos += pos;
|
|
break;
|
|
case SEEK_END:
|
|
curpos = theHandleFiles[fd].size - pos;
|
|
break;
|
|
default: /* set */
|
|
curpos = pos;
|
|
break;
|
|
}
|
|
|
|
if (curpos < 0)
|
|
curpos = 0;
|
|
else if (curpos > theHandleFiles[fd].size)
|
|
curpos = theHandleFiles[fd].size;
|
|
|
|
theHandleFiles[fd].mark = curpos;
|
|
|
|
return curpos;
|
|
}
|
|
|
|
void
|
|
C2P(const char *c, unsigned char *p)
|
|
{
|
|
int len = strlen(c), i;
|
|
|
|
if (len > 255)
|
|
len = 255;
|
|
|
|
for (i = len; i > 0; i--)
|
|
p[i] = c[i - 1];
|
|
p[0] = len;
|
|
}
|
|
|
|
void
|
|
P2C(const unsigned char *p, char *c)
|
|
{
|
|
int idx = *p++;
|
|
for (; idx > 0; idx--)
|
|
*c++ = *p++;
|
|
*c = '\0';
|
|
}
|
|
|
|
static void
|
|
replace_resource(Handle new_res, ResType its_type, short its_id,
|
|
Str255 its_name)
|
|
{
|
|
Handle old_res;
|
|
|
|
SetResLoad(false);
|
|
old_res = Get1Resource(its_type, its_id);
|
|
SetResLoad(true);
|
|
if (old_res) {
|
|
RemoveResource(old_res);
|
|
DisposeHandle(old_res);
|
|
}
|
|
|
|
AddResource(new_res, its_type, its_id, its_name);
|
|
}
|
|
|
|
int
|
|
maccreat(const char *name, long fileType)
|
|
{
|
|
return macopen(name, O_RDWR | O_CREAT | O_TRUNC, fileType);
|
|
}
|
|
|
|
int
|
|
macopen(const char *name, int flags, long fileType)
|
|
{
|
|
short refNum;
|
|
short perm;
|
|
Str255 s;
|
|
|
|
C2P(name, s);
|
|
if (flags & O_CREAT) {
|
|
if (HCreate(theDirs.dataRefNum, theDirs.dataDirID, s, TEXT_CREATOR,
|
|
fileType) && (flags & O_EXCL)) {
|
|
return -1;
|
|
}
|
|
#if 0 /* Fails during makedefs */
|
|
if (fileType == SAVE_TYPE) {
|
|
short resRef;
|
|
HCreateResFile(theDirs.dataRefNum, theDirs.dataDirID, s);
|
|
resRef = HOpenResFile(theDirs.dataRefNum, theDirs.dataDirID, s,
|
|
fsRdWrPerm);
|
|
if (resRef != -1) {
|
|
Handle name;
|
|
Str255 plnamep;
|
|
|
|
C2P(svp.plname, plnamep);
|
|
name = (Handle)NewString(plnamep);
|
|
if (name)
|
|
replace_resource(name, 'STR ', PLAYER_NAME_RES_ID,
|
|
"\pPlayer Name");
|
|
|
|
/* The application name resource. See IM VI, page 9-21. */
|
|
name = (Handle)GetString(APP_NAME_RES_ID);
|
|
if (name) {
|
|
DetachResource(name);
|
|
replace_resource(name, 'STR ', APP_NAME_RES_ID,
|
|
"\pApplication Name");
|
|
}
|
|
|
|
CloseResFile(resRef);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
/*
|
|
* Here, we should check for file type, maybe a SFdialog if
|
|
* we fail with default, etc. etc. Besides, we should use HOpen
|
|
* and permissions.
|
|
*/
|
|
if ((flags & O_RDONLY) == O_RDONLY) {
|
|
perm = fsRdPerm;
|
|
}
|
|
if ((flags & O_WRONLY) == O_WRONLY) {
|
|
perm = fsWrPerm;
|
|
}
|
|
if ((flags & O_RDWR) == O_RDWR) {
|
|
perm = fsRdWrPerm;
|
|
}
|
|
if (HOpen(theDirs.dataRefNum, theDirs.dataDirID, s, perm, &refNum)) {
|
|
return OpenHandleFile(s, fileType);
|
|
}
|
|
if (flags & O_TRUNC) {
|
|
if (SetEOF(refNum, 0L)) {
|
|
FSClose(refNum);
|
|
return -1;
|
|
}
|
|
}
|
|
return refNum;
|
|
}
|
|
|
|
int
|
|
macclose(int fd)
|
|
{
|
|
if (IsHandleFile(fd)) {
|
|
CloseHandleFile(fd);
|
|
} else {
|
|
if (FSClose(fd)) {
|
|
return -1;
|
|
}
|
|
FlushVol((StringPtr) 0, theDirs.dataRefNum);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
macread(int fd, void *ptr, unsigned len)
|
|
{
|
|
long amt = len;
|
|
|
|
if (IsHandleFile(fd)) {
|
|
return ReadHandleFile(fd, ptr, amt);
|
|
} else {
|
|
short err = FSRead(fd, &amt, ptr);
|
|
|
|
return ((err == noErr) || (err == eofErr && len)) ? amt : -1;
|
|
}
|
|
}
|
|
|
|
#if 0 /* this function isn't used, if you use it, uncomment prototype in \
|
|
macwin.h */
|
|
char *
|
|
macgets (int fd, char *ptr, unsigned len)
|
|
{
|
|
int idx = 0;
|
|
char c;
|
|
|
|
while (-- len > 0) {
|
|
if (macread (fd, ptr + idx, 1) <= 0)
|
|
return (char *)0;
|
|
c = ptr[idx++];
|
|
if (c == '\n' || c == '\r')
|
|
break;
|
|
}
|
|
ptr [idx] = '\0';
|
|
return ptr;
|
|
}
|
|
#endif /* 0 */
|
|
|
|
int
|
|
macwrite(int fd, void *ptr, unsigned len)
|
|
{
|
|
long amt = len;
|
|
|
|
if (IsHandleFile(fd))
|
|
return -1;
|
|
if (FSWrite(fd, &amt, ptr) == noErr)
|
|
return (amt);
|
|
else
|
|
return (-1);
|
|
}
|
|
|
|
long
|
|
macseek(int fd, long where, short whence)
|
|
{
|
|
short posMode;
|
|
long curPos;
|
|
|
|
if (IsHandleFile(fd)) {
|
|
return SetHandleFilePos(fd, whence, where);
|
|
}
|
|
|
|
switch (whence) {
|
|
default:
|
|
posMode = fsFromStart;
|
|
break;
|
|
case SEEK_CUR:
|
|
posMode = fsFromMark;
|
|
break;
|
|
case SEEK_END:
|
|
posMode = fsFromLEOF;
|
|
break;
|
|
}
|
|
|
|
if (SetFPos(fd, posMode, where) == noErr && GetFPos(fd, &curPos) == noErr)
|
|
return (curPos);
|
|
else
|
|
return (-1);
|
|
}
|
|
|
|
int
|
|
macunlink(const char *name)
|
|
{
|
|
Str255 pname;
|
|
|
|
C2P(name, pname);
|
|
return (HDelete(theDirs.dataRefNum, theDirs.dataDirID, pname) == noErr
|
|
? 0
|
|
: -1);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
boolean
|
|
rsrc_dlb_init(void)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
rsrc_dlb_cleanup(void)
|
|
{
|
|
}
|
|
|
|
boolean
|
|
rsrc_dlb_fopen(dlb *dp, const char *name, const char *mode)
|
|
{
|
|
#if defined(__SC__) || defined(__MRC__)
|
|
#pragma unused(mode)
|
|
#endif
|
|
Str255 pname;
|
|
|
|
C2P(name, pname);
|
|
dp->fd = OpenHandleFile(pname, 'File'); /* automatically read-only */
|
|
return dp->fd >= 0;
|
|
}
|
|
|
|
int
|
|
rsrc_dlb_fclose(dlb *dp)
|
|
{
|
|
return CloseHandleFile(dp->fd);
|
|
}
|
|
|
|
int
|
|
rsrc_dlb_fread(char *buf, int size, int quan, dlb *dp)
|
|
{
|
|
int nread;
|
|
|
|
if (size < 0 || quan < 0)
|
|
return 0;
|
|
nread = ReadHandleFile(dp->fd, buf, (unsigned) size * (unsigned) quan);
|
|
|
|
return nread / size; /* # of whole pieces (== quan in normal case) */
|
|
}
|
|
|
|
int
|
|
rsrc_dlb_fseek(dlb *dp, long pos, int whence)
|
|
{
|
|
return SetHandleFilePos(dp->fd, whence, pos);
|
|
}
|
|
|
|
char *
|
|
rsrc_dlb_fgets(char *buf, int len, dlb *dp)
|
|
{
|
|
HandleFile *hfp = IsHandleFile(dp->fd);
|
|
char *p;
|
|
int bytesLeft, n = 0;
|
|
|
|
if (hfp && hfp->mark < hfp->size) {
|
|
bytesLeft = hfp->size - hfp->mark;
|
|
if (bytesLeft < len)
|
|
len = bytesLeft;
|
|
|
|
HLock(hfp->data);
|
|
for (n = 0, p = *hfp->data + hfp->mark; n < len; n++, p++) {
|
|
buf[n] = *p;
|
|
if (*p == '\r')
|
|
buf[n] = '\n';
|
|
if (buf[n] == '\n') {
|
|
n++; /* we want the return in the buffer */
|
|
break;
|
|
}
|
|
}
|
|
HUnlock(hfp->data);
|
|
|
|
hfp->mark += n;
|
|
if (n != 0)
|
|
buf[n] = '\0'; /* null terminate result */
|
|
}
|
|
|
|
return n ? buf : NULL;
|
|
}
|
|
|
|
int
|
|
rsrc_dlb_fgetc(dlb *dp)
|
|
{
|
|
HandleFile *hfp = IsHandleFile(dp->fd);
|
|
int ret;
|
|
|
|
if (!hfp || hfp->size <= hfp->mark)
|
|
return EOF;
|
|
|
|
ret = *(unsigned char *) (*hfp->data + hfp->mark);
|
|
hfp->mark++;
|
|
return ret;
|
|
}
|
|
|
|
long
|
|
rsrc_dlb_ftell(dlb *dp)
|
|
{
|
|
HandleFile *hfp = IsHandleFile(dp->fd);
|
|
|
|
if (!hfp)
|
|
return 0;
|
|
return hfp->mark;
|
|
}
|