/* Mac-specific code for Xconq kernel. Copyright (C) 1992-1997, 1999 Stanley T. Shebs. Xconq is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. See the file COPYING. */ /* The code in this file is Mac-specific, but not necessarily specific to any particular app; it should be usable with utilities and with apps that don't have fancy interface. */ #include "config.h" #include "misc.h" #include "dir.h" #include "lisp.h" #include "module.h" #include "system.h" #if 0 /* Unused. */ void mac_abort(void); #endif /* Following are from tp.h, but tp.h is too high-level to include. */ #if 0 /* shouldn't be here */ extern int numremotes; extern int my_rid; extern int master_rid; #endif extern void init_file_port(int willhost); extern void close_file_port(void); extern void low_file_send(int id, char *buf); extern int low_file_receive(int *id, char *buf, int maxchars, int timeout); /* Private functions. */ static void init_serial_port(int port); static void close_serial_port(void); static void open_appletalk(void); static int low_appletalk_receive(int *id, char *buf, int maxchars, int timout); static void low_appletalk_send(int id, char *buf); static void close_appletalk(void); static void low_serial_send(int id, char *buf); static int low_serial_readchars(char *buf, int maxchars, int timeout); #ifdef THINK_C #include #endif /* THINK_C */ #ifdef MPW #ifdef NEW_HEADERS #include #else #include #endif /* NEW_HEADERS */ #include #include /* for TickCount */ #include /* for GetIndString */ #endif /* MPW */ #ifdef __MWERKS__ #ifdef NEW_HEADERS #include #else #include #endif /* NEW_HEADERS */ #include #include /* for TickCount */ #include /* for GetIndString */ #include #include #include #endif /* __MWERKS__ */ #include /* We need this in order to find string resources that have filenames in them. */ #include "macdefs.h" #include #include #ifdef MPW_C #define QD(whatever) (qd.##whatever) #define QDPat(whatever) (&(qd.##whatever)) #endif #ifdef THINK_C #define QD(whatever) (whatever) #define QDPat(whatever) (whatever) #endif #ifdef __MWERKS__ #define QD(whatever) (qd.##whatever) #define QDPat(whatever) (&(qd.##whatever)) #endif #ifndef c2p #define c2p(STR,PBUF) \ strcpy(((char *) PBUF) + 1, STR); \ PBUF[0] = strlen(STR); #endif #ifndef p2c #define p2c(PSTR,BUF) \ strncpy(BUF, ((char *) (PSTR) + 1), PSTR[0]); \ BUF[PSTR[0]] = '\0'; #endif extern CursHandle sendcursor; extern CursHandle receivecursor; extern CursHandle current_cursor; int connection_method; /* The HFS volume that the program started with. */ short initialvrefnum; static char *news_fname; static char *save_fname; static char *checkpoint_fname; static char *error_save_fname; static char *statistics_fname; /* We normally keep the library and image folders in the same folder as the app. */ #ifndef XCONQLIB #define XCONQLIB ":lib" #endif #ifndef XCONQIMAGES #define XCONQIMAGES ":images" #endif char * default_library_pathname() { return XCONQLIB; } char * default_images_pathname() { return XCONQIMAGES; } char * news_filename() { Str255 tmpstr; char tmpbuf[255]; if (news_fname == NULL) { GetIndString(tmpstr, sFilenames, siNews); p2c(tmpstr, tmpbuf); if (!empty_string(tmpbuf)) news_fname = copy_string(tmpbuf); else news_fname = NEWSFILE; } return news_fname; } char * saved_game_filename() { Str255 tmpstr; char tmpbuf[255]; if (save_fname == NULL) { GetIndString(tmpstr, sFilenames, siSavedGame); p2c(tmpstr, tmpbuf); if (!empty_string(tmpbuf)) save_fname = copy_string(tmpbuf); else save_fname = "Saved Game"; } return save_fname; } /* (should make use of turn number in checkpoint filename) */ char * checkpoint_filename(int n) { Str255 tmpstr; char tmpbuf[255]; if (checkpoint_fname == NULL) { GetIndString(tmpstr, sFilenames, siCheckpoint); p2c(tmpstr, tmpbuf); if (!empty_string(tmpbuf)) checkpoint_fname = copy_string(tmpbuf); else checkpoint_fname = "Checkpoint"; } return checkpoint_fname; } char * error_save_filename() { Str255 tmpstr; char tmpbuf[255]; if (error_save_fname == NULL) { GetIndString(tmpstr, sFilenames, siErrorSave); p2c(tmpstr, tmpbuf); if (!empty_string(tmpbuf)) error_save_fname = copy_string(tmpbuf); else error_save_fname = "Error Save"; } return error_save_fname; } char * statistics_filename() { Str255 tmpstr; char namebuf[256]; Point pnt; SFReply reply; if (statistics_fname == NULL) { GetIndString(tmpstr, sFilenames, siStatistics); p2c(tmpstr, namebuf); if (!empty_string(namebuf)) statistics_fname = copy_string(namebuf); else statistics_fname = "Statistics"; /* Collect the file and path to save to. */ SetPt(&pnt, 100, 100); c2p(statistics_fname, tmpstr); SFPutFile(pnt, "\pSave game statistics as:", tmpstr, /*(DlgHookProcPtr)*/ nil, &reply); if (!reply.good) return NULL; /* Make the location of the file be the current volume. */ SetVol(reply.fName, reply.vRefNum); p2c(((char *) reply.fName), namebuf); statistics_fname = copy_string(namebuf); } return statistics_fname; } /* The purpose of this wrapper for fopen is to handle cases where the mac is opening a unix file and automatic linefeed conversion therefore should be disabled. */ FILE * open_file(char *filename, char *mode) { FILE *fp; short curvrefnum; int mac_linefeeds = 0; int unix_linefeeds = 0; int ch; /* Hack. Force debug output to top directory. */ if (strcmp(filename, "Xconq.DebugOut") == 0) SetVol(NULL, initialvrefnum); /* Hack. Force warning output to top directory. */ if (strcmp(filename, "Xconq.Warnings") == 0) SetVol(NULL, initialvrefnum); /* Writing or Appending: always mac format. */ if (strcmp(mode, "w") == 0) { fp = fopen(filename, "w"); return fp; } if (strcmp(mode, "a") == 0) { fp = fopen(filename, "a"); return fp; } /* Reading: first open in binary mode (no conversion). */ fp = fopen(filename, "rb"); /* Look in xconq start directory if we did not find a file. */ if (fp == NULL) { GetVol(NULL, &curvrefnum); SetVol(NULL, initialvrefnum); fp = fopen(filename, "rb"); SetVol(NULL, curvrefnum); } /* Give up if we found nothing there either. */ if (fp == NULL) return fp; /* Binary (image) files don't need conversion. */ if (strcmp(mode, "rb") == 0) return fp; /* Text file. Count the number of linefeeds of each kind. */ while ((ch = getc(fp)) != EOF) { if (ch == '\r') ++mac_linefeeds; if (ch == '\n') ++unix_linefeeds; } /* Close the file (also needed in unix case to reset file pointer). */ fclose(fp); /* Assume that a mac file has more mac than unix linefeeds. */ if (mac_linefeeds > unix_linefeeds) { /* Reopen mac file in text mode (with conversion). */ fp = fopen(filename, "r"); } else { /* Reopen unix file in binary mode (no conversion). */ fp = fopen(filename, "rb"); } return fp; } /* Attempt to open a library file. */ FILE * open_module_library_file(Module *module) { short curvrefnum; char fullnamebuf[255]; LibraryPath *p; FILE *fp; /* Can't open anonymous library modules. */ if (module->name == NULL) return NULL; /* Generate library pathname. */ for_all_library_paths(p) { make_pathname(p->path, module->name, "g", fullnamebuf); /* Now try to open the file. */ fp = open_file(fullnamebuf, "r"); if (fp != NULL) { /* Remember the filename where we found it. */ module->filename = copy_string(fullnamebuf); return fp; } } return NULL; } FILE * open_module_explicit_file(Module *module) { short curvrefnum; char fullnamebuf[255]; LibraryPath *p; FILE *fp = NULL; if (module->filename == NULL) { /* Try guessing a filename, since none supplied. */ if (module->name != NULL) { for_all_library_paths(p) { make_pathname(p->path, module->name, "g", fullnamebuf); /* Now try to open the file. */ fp = open_file(fullnamebuf, "r"); if (fp != NULL) return fp; } } } else { /* Try some other random ideas. */ sprintf(fullnamebuf, "%s", module->filename); fp = open_file(module->filename, "r"); if (fp != NULL) { add_library_path(""); return fp; } sprintf(fullnamebuf, ":%s", module->filename); fp = open_file(fullnamebuf, "r"); if (fp != NULL) return fp; sprintf(fullnamebuf, "%s%s", ":lib:", module->filename); fp = open_file(fullnamebuf, "r"); if (fp != NULL) return fp; } return NULL; } FILE * open_library_file(char *filename) { short curvrefnum; char fullnamebuf[255]; LibraryPath *p; FILE *fp; /* Now try to open the file. */ fp = open_file(filename, "r"); if (fp != NULL) return fp; /* Generate library pathname. */ for_all_library_paths(p) { make_pathname(p->path, filename, NULL, fullnamebuf); /* Now try to open the file. */ fp = open_file(fullnamebuf, "r"); if (fp != NULL) return fp; } return NULL; } FILE * open_scorefile_for_reading(char *name) { char buf[255]; short curvrefnum; FILE *fp; GetVol(NULL, &curvrefnum); SetVol(NULL, initialvrefnum); fp = open_file(name, "r"); SetVol(NULL, curvrefnum); return fp; } FILE * open_scorefile_for_writing(char *name) { char buf[255]; short curvrefnum; FILE *fp; GetVol(NULL, &curvrefnum); SetVol(NULL, initialvrefnum); /* Now try to open the file. */ sprintf(buf, "%s", name); fp = open_file(buf, "a"); SetVol(NULL, curvrefnum); return fp; } /* Close scorefile after having written to it; nothing special to do on Macs. */ void close_scorefile_for_writing(fp) FILE *fp; { fclose(fp); } void make_pathname(char *path, char *name, char *extn, char *pathbuf) { strcpy(pathbuf, ""); if (!empty_string(path)) { strcat(pathbuf, path); strcat(pathbuf, ":"); } strcat(pathbuf, name); /* Don't add a second identical extension, but do add if extension is different (in case we want "foo.12" -> "foo.12.g" for instance) */ if (strrchr(name, '.') && extn && strcmp(strrchr(name, '.') + 1, extn) == 0) return; if (!empty_string(extn)) { strcat(pathbuf, "."); strcat(pathbuf, extn); } } /* Remove a given file. */ int remove_file(char *fname) { /* (should implement) */ return FALSE; } void init_signal_handlers() { } int last_ticks = 0; int n_seconds_elapsed(n) int n; { int ticks = TickCount(); if (((ticks - last_ticks) / 60) > n) { last_ticks = ticks; return TRUE; } else { return FALSE; } } int last_ticks_for_ms = 0; int n_ms_elapsed(n) int n; { return (((TickCount() - last_ticks_for_ms) * 16) > n); } void record_ms() { last_ticks_for_ms = TickCount(); } #if 0 /* Not called from anywhere. */ /* Instead of coredumping, which is not a normal Mac facility, we drop into Macsbug. If we then "g" from Macsbug, the program will exit cleanly. */ void mac_abort () { /* Make sure no output still buffered up, then zap into MacsBug. */ #if 0 /* how to know if stdio in use? */ fflush(stdout); fflush(stderr); printf("## Abort! ##\n"); #endif #ifdef MPW_SADE SysError(8005); #else Debugger(); #endif /* "g" in MacsBug will then cause a regular error exit. */ exit(1); } #endif int open_remote_connection(char *methodname, int willhost) { int serial_port, rslt; if (methodname == NULL) { connection_method = 0; } else if (strcmp(methodname, "serial") == 0) { connection_method = 1; } else if (strcmp(methodname, "appletalk") == 0) { connection_method = 2; } else if (strcmp(methodname, "tcp") == 0) { connection_method = 3; } else if (strcmp(methodname, "file") == 0) { connection_method = 4; } else { connection_method = 0; } switch (connection_method) { case 0: break; case 1: /* Get a decision about which serial port to use. */ serial_port = serial_port_dialog(); /* If cancelled here, get out and don't open anything. */ if (serial_port < 0) break; init_serial_port(serial_port); /* Serial supports exactly two programs, so we can easily set up all the rids right now. */ #if 0 /* but that would be wrong */ master_rid = 1; if (willhost) { my_rid = 1; } else { my_rid = 2; } numremotes = 2; #endif break; case 2: open_appletalk(); break; case 3: init_warning("No TCP/IP support yet, ignoring"); break; case 4: init_file_port(willhost); break; default: break; } return connection_method; } extern short ser_input_refnum; extern short ser_output_refnum; /* Low-level transmission to another program. */ void low_send(int id, char *buf) { if (sendcursor != nil) SetCursor(*sendcursor); Dprintf("Sending: %d \"%s\"...", id, (buf ? buf : "")); switch (connection_method) { case 0: /* Not connected anywhere, no need to complain. */ break; case 1: low_serial_send(id, buf); break; case 2: low_appletalk_send(id, buf); break; case 3: break; case 4: low_file_send(id, buf); break; default: case_panic("connection method", connection_method); break; } Dprintf(" sent.\n"); if (sendcursor != nil) { if (current_cursor != nil) SetCursor(*current_cursor); else SetCursor(&QD(arrow)); } } int low_receive(int *idp, char *buf, int maxchars, int timeout) { int rslt = FALSE; if (receivecursor != nil) SetCursor(*receivecursor); switch (connection_method) { case 0: /* Not connected anywhere, no need to complain. */ break; case 1: /* Only one remote if using serial. */ *idp = (0 /*my_rid == 1 */? 2 : 1); rslt = low_serial_readchars(buf, maxchars, timeout); break; case 2: /* Only one remote for now. */ *idp = (0 /*my_rid == 1*/ ? 2 : 1); rslt = low_appletalk_receive(idp, buf, maxchars, timeout); break; case 3: break; case 4: rslt = low_file_receive(idp, buf, maxchars, timeout); break; default: case_panic("connection method", connection_method); break; } if (receivecursor != nil) { if (current_cursor != nil) SetCursor(*current_cursor); else SetCursor(&QD(arrow)); } return rslt; } void close_remote_connection(int rid) { switch (connection_method) { case 0: break; case 1: close_serial_port(); break; case 2: close_appletalk(); break; case 3: break; case 4: close_file_port(); break; default: break; } } short ser_input_refnum; short ser_output_refnum; static void init_serial_port(port) int port; { OSErr err; if (port == 0) { /* Open the modem port. */ err = OpenDriver("\p.AIn", &ser_input_refnum); if (err != 0) { return; } err = OpenDriver("\p.AOut", &ser_output_refnum); if (err != 0) { /* This isn't working, so close the input as well. */ CloseDriver(ser_input_refnum); return; } } else { /* Open the printer port. */ err = OpenDriver("\p.BIn", &ser_input_refnum); if (err != 0) { return; } err = OpenDriver("\p.BOut", &ser_output_refnum); if (err != 0) { /* This isn't working, so close the input as well. */ CloseDriver(ser_input_refnum); return; } } #if 0 /* might need this eventually? */ if (0 /* using custom buffer */) SerSetBuf (ser_input_refnum, mac_input_buffer, 256); #endif SerReset (ser_input_refnum, stop10|noParity|data8|baud9600); SerReset (ser_output_refnum, stop10|noParity|data8|baud9600); /* (what does this do?) */ { CntrlParam cb; struct SerShk *handshake; cb.ioCRefNum = ser_output_refnum; cb.csCode = 14; handshake = (struct SerShk *) &cb.csParam[0]; handshake->fXOn = 0; handshake->fCTS = 0; handshake->xOn = 0; handshake->xOff = 0; handshake->errs = 0; handshake->evts = 0; handshake->fInX = 0; handshake->fDTR = 0; err = PBControl ((ParmBlkPtr) &cb, 0); } } static void low_serial_send(int id, char *buf) { OSErr err; IOParam pb; pb.ioRefNum = ser_output_refnum; pb.ioBuffer = (Ptr) buf; pb.ioReqCount = strlen(buf); err = PBWrite((ParmBlkPtr) &pb, 0); } static int low_serial_readchars(char *buf, int maxchars, int timeout) { int status, n, n2; /* time_t */ unsigned long start_time, now; OSErr err; CntrlParam cb; IOParam pb; time (&start_time); while (1) { cb.ioCRefNum = ser_input_refnum; cb.csCode = 2; err = PBStatus((ParmBlkPtr) &cb, 0); if (err < 0) return FALSE; n = *((long *) &cb.csParam[0]); if (n > 0) { pb.ioRefNum = ser_input_refnum; pb.ioBuffer = (Ptr) buf; pb.ioReqCount = (n > 64 ? 64 : n); err = PBRead((ParmBlkPtr) &pb, 0); if (err < 0) return FALSE; n2 = pb.ioReqCount; buf[n2] = '\0'; return TRUE; } else if (timeout == 0) { return FALSE; } else if (timeout == -1) { /* Go around again. */ } else { time (&now); if (now > start_time + timeout) { Dprintf("%ul > %ul + %d\n", now, start_time, timeout); return FALSE /* timed out */; } } } } static void close_serial_port() { if (ser_input_refnum) CloseDriver(ser_input_refnum); if (ser_output_refnum) CloseDriver(ser_output_refnum); } NamesTableEntry names_table_entry; int nbp_registered; unsigned char atp_socket_number; AddrBlock remote_addr; static void open_appletalk() { #ifndef __MWERKS__ int i, numgotten; OSErr err; ATPParamBlock tmpATPPB; MPPParamBlock tmpMPPPB, tmpCancelMPPPB; NamesTableEntry lookup; char *namesbuffer; char *bigbuffer; EntityName entityname; AddrBlock entityaddr; if (IsMPPOpen == false) { err = MPPOpen(); /* (should check result) */ } tmpMPPPB.SETSELF.newSelfFlag = true; err = PSetSelfSend(&tmpMPPPB, false); /* (should check result) */ /* (should collect our own network address?) */ tmpATPPB.ATP.atpSocket = 0; tmpATPPB.ATP.addrBlock.aNet = 0; tmpATPPB.ATP.addrBlock.aNode = 0; tmpATPPB.ATP.addrBlock.aSocket = 0; err = POpenATPSkt(&tmpATPPB, false); /* (should check result) */ NBPSetNTE(&names_table_entry, "\pStan", "\pXconq", "\p*", 64); tmpMPPPB.NBP.ioCompletion = nil; tmpMPPPB.NBP.interval = 0x0F; tmpMPPPB.NBP.count = 0x03; #if 0 tmpMPPPB.NBP.nbpPtrs.entityPtr = &names_table_entry; #else tmpMPPPB.NBP.NBPPtrs.entityPtr = &names_table_entry; #endif tmpMPPPB.NBP.parm.verifyFlag = true; err = PRegisterName(&tmpMPPPB, true); /* (should check result) */ { short itemHit; EventRecord theEvent; Point mouse; extern void get_global_mouse(Point *mouse); do { itemHit = 0; if (GetNextEvent(everyEvent,&theEvent) == true && IsDialogEvent(&theEvent) == true) { get_global_mouse(&mouse); if (mouse.v == 0 && mouse.h == 0) itemHit = 1; else { itemHit = 0; } } } while (itemHit != 1 && tmpMPPPB.NBP.ioResult == 1); if (itemHit == 1) { tmpCancelMPPPB.NBPKILL.nKillQEl = (Ptr) &tmpMPPPB; PKillNBP(&tmpCancelMPPPB, false); } else nbp_registered = TRUE; } bigbuffer = xmalloc(10000); namesbuffer = xmalloc(250 * 33); NBPSetEntity((Ptr)&lookup.nt.entityData, "\p=", "\pXconq", "\p*"); tmpMPPPB.NBP.ioCompletion = nil; tmpMPPPB.NBP.interval = 3; tmpMPPPB.NBP.count = 3; tmpMPPPB.NBPentityPtr = &lookup.nt.entityData; tmpMPPPB.NBPretBuffSize = 10000; tmpMPPPB.NBPretBuffPtr = bigbuffer; tmpMPPPB.NBPmaxToGet = 1000 / sizeof(NTElement); err = PLookupName(&tmpMPPPB, false); numgotten = tmpMPPPB.NBP.parm.Lookup.numGotten; if (numgotten > 0) { #if 0 master_rid = 1; if (willhost) { my_rid = 1; } else { my_rid = 2; } numremotes = 1; #endif /* (should use this loop to get actual number of remotes) */ for (i = 0; i < numgotten; ++i) { err = NBPExtract(namesbuffer, numgotten, i + 1, &entityname, &remote_addr); } } /* (should release big buffers) */ /* Open an ATP socket. */ tmpATPPB.ATP.atpSocket = 0; tmpATPPB.ATP.addrBlock.aNet = 0; tmpATPPB.ATP.addrBlock.aNode = 0; tmpATPPB.ATP.addrBlock.aSocket = 0; err = POpenATPSkt(&tmpATPPB, false); atp_socket_number = tmpATPPB.ATP.atpSocket; #endif /* __MWERKS__ */ } static int low_appletalk_receive(int *idp, char *buf, int maxchars, int timeout) { #ifndef __MWERKS__ int status, n; /* time_t */ unsigned long start_time, now; OSErr err; int numbds; char atprequestbuf[256]; BDSType bds; ATPParamBlock requestATPPB, replyATPPB; time (&start_time); while (1) { requestATPPB.ATP.ioCompletion = nil; requestATPPB.ATP.atpSocket = atp_socket_number; requestATPPB.ATP.reqLength = maxchars; requestATPPB.ATP.reqPointer = buf; err = PGetRequest(&requestATPPB, false); numbds = BuildBDS("OK", &bds, 256); replyATPPB.ATP.ioCompletion = nil; replyATPPB.ATP.atpFlags = atpEOMvalue; replyATPPB.ATP.atpSocket = atp_socket_number; replyATPPB.ATP.addrBlock = requestATPPB.ATP.addrBlock; replyATPPB.ATP.reqLength = strlen(buf) + 1; replyATPPB.ATP.bdsPointer = &bds; replyATPPB.OTH1.u0.numOfBuffs = 1; replyATPPB.OTH2.bdsSize = 256; replyATPPB.OTH2.transID = requestATPPB.ATP.reqTID; err = PSendResponse(&replyATPPB, false); n = requestATPPB.ATP.reqLength; if (n > 0) { buf[n] = '\0'; return TRUE; } else if (timeout == 0) { return FALSE; } else if (timeout == -1) { /* Go around again. */ } else { time (&now); if (now > start_time + timeout) { Dprintf("%ul > %ul + %d\n", now, start_time, timeout); return FALSE /* timed out */; } } } #else return 0; #endif /* __MWERKS__ */ } static void low_appletalk_send(int id, char *buf) { #ifndef __MWERKS__ int numbds; char atpresponsebuf[256]; OSErr err; BDSType bds; ATPParamBlock tmpATPPB; numbds = BuildBDS(atpresponsebuf, &bds, 256); tmpATPPB.ATP.ioCompletion = nil; tmpATPPB.ATP.atpFlags = 0; tmpATPPB.ATP.atpSocket = atp_socket_number; tmpATPPB.ATP.addrBlock.aNet = remote_addr.aNet; tmpATPPB.ATP.addrBlock.aNode = remote_addr.aNode; tmpATPPB.ATP.addrBlock.aSocket = remote_addr.aSocket; tmpATPPB.ATP.reqLength = strlen(buf) + 1; tmpATPPB.ATP.reqPointer = buf; tmpATPPB.ATP.bdsPointer = &bds; tmpATPPB.SREQ.timeOutVal = 10; tmpATPPB.SREQ.retryCount = 3; err = PSendRequest(&tmpATPPB, false); #endif /* __MWERKS __ */ } static void close_appletalk() { #ifndef __MWERKS__ OSErr err; ATPParamBlock tmpATPPB; MPPParamBlock tmpMPPPB; tmpATPPB.ATP.atpSocket = atp_socket_number; err = PCloseATPSkt(&tmpATPPB, false); if (nbp_registered) { tmpMPPPB.NBP.NBPPtrs.entityPtr = (Ptr) &names_table_entry.nt.entityData; err = PRemoveName(&tmpMPPPB, false); nbp_registered = FALSE; } #endif /* __MWERKS __ */ }