home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Fred Fish Collection 1.5
/
ffcollection-1-5-1992-11.iso
/
ff_disks
/
500-599
/
ff502.lzh
/
CELLS
/
CELLSSource.lzh
/
cRead.c
< prev
next >
Wrap
C/C++ Source or Header
|
1991-04-20
|
21KB
|
780 lines
/*
* CELLS An Implementation of the WireWorld cellular automata
* as described in Scientific American, Jan 1990.
*
* Copyright 1990 by Davide P. Cervone.
* You may use this code, provided this copyright notice is kept intact.
* See the CELLS.HELP file for complete information on distribution conditions.
*/
/*
* File: cRead.c Handles reading Ciruit and Library files.
*/
#include "cGadget.h"
#include "cRequest.h"
#include "cParts.h"
#include <stdio.h>
char FileName[MAXFNAME]; /* current circuit's name */
static FILE *theFile; /* the file being read */
static int noError,EndOfFile; /* error flags */
static char Line[256]; /* input buffer */
static char *NextWord,*Parameters; /* pointers to parts of commands */
static short LinePos; /* current character being read */
static int TooWide; /* TRUE if circuit is too wide */
static LIBRARY *LibList; /* list of libraries to load */
#define MAXERROR 8
#define CELL_UNKNOWN 0xFF
#define COM_UNKNOWN 0
#define COM_CIRCUIT 1
#define COM_LIBRARY 2
#define COM_PART 3
#define COM_CELLSIZE 4
#define COM_ORIGIN 5
#define COM_VIEW 6
#define COM_INCLUDE 7
#define COM_BLANKLINE 8
#define COM_CELLS 9
#define COM_EOF 10
/*
* Command character strings
*/
static char *Command[] =
{"Circuit","Library","Part","CellSize","Origin","View","Include"};
static char LoadMessage[] = "Continuing with Load...";
#define LOADMESSAGE LoadMessage
/*
* ReadError()
*
* Put up the error message and increment the error count
* If too many errors occurred, ask if the user wants to quit loading
* If not, put up the info message again
*/
void ReadError(s,x1,x2,x3)
char *s,*x1,*x2,*x3;
{
static int ErrorCount = 0;
DoError(s,x1,x2,x3);
ErrorCount++;
if (ErrorCount == MAXERROR)
{
noError = DoQuestion("%d Errors: Continue Loading?",ErrorCount);
ErrorCount = 0;
}
if (noError) DoInfoMessage(LOADMESSAGE);
}
/*
* ReadNextLine()
*
* If no errors have occurred
* If we can read another line from the file
* set the line position to the start of the line
* otherwise
* if we're at the end of the file, indicate that
* otherwise put up an error message
* Otherwise consider ourselves at the end of the file (don't read any more)
*/
static void ReadNextLine()
{
if (noError)
{
if (fgets(Line,256,theFile))
{
LinePos = 0;
} else {
if (feof(theFile))
{
EndOfFile = TRUE;
} else {
DosError("Read","Line");
noError = FALSE;
}
}
} else {
EndOfFile = TRUE;
}
}
/*
* GetNextWord()
*
* While still looking and not at the end of the file
* if the the rest of the line is a comment, skip it
* if the next character is a space or tab, go to the next character
* otherwise we've found the first letter of the next word
* Mark the start of the word
* Find the end of the word and mark it as the beginning of the parameters
* Find the trailing '\n' and convert it to a NULL
*/
static void GetNextWord()
{
int NotDone = TRUE;
char *s;
while (NotDone && !EndOfFile)
{
if (Line[LinePos] == '*' || Line[LinePos] == '!') ReadNextLine();
else if (Line[LinePos] == ' ' || Line[LinePos] == '\t') LinePos++;
else NotDone = FALSE;
}
NextWord = &Line[LinePos];
while (Line[LinePos] != ' ' && Line[LinePos] != '\n' && Line[LinePos])
LinePos++;
s = Parameters = &Line[LinePos+1];
while (*s && *s != '\n') s++;
if (*s == '\n') *s = 0;
}
/*
* GetNextCommand()
*
* Get the next word and its parameters
* If we're not at the end of the file
* check if the first character of the command is a state symbol; if not
* if we actually have some characters
* temporarily mark the end of the word with a NULL
* and look through the command list for a match
* then put back the original separating character
* (if no match is made, i will be decremented to COM_UNKNOWN)
*/
static int GetNextCommand()
{
short i = COM_EOF;
int NotDone = TRUE;
char c;
GetNextWord();
if (!EndOfFile)
{
i--; c = *NextWord;
if (c != '[' && c != '=' && c != '#' && c != '.')
{
i--;
if (*NextWord && *NextWord != '\n')
{
c = Line[LinePos]; Line[LinePos] = 0;
while (i>0 && NotDone)
if (stricmp(NextWord,Command[i-1]) == 0)
NotDone = FALSE; else i--;
Line[LinePos] = c;
}
}
}
return(i);
}
/*
* AddLineToGrid()
*
* Start at the beginning of the line
* If the current Y position is past the bottom of the grid, give an error
* otherwise
* while there is more to do on the line
* find the cell type of the next character on the line:
* if we're at the end of the line, we're done
* '[]' = WIRE '##' = HEAD '==' = TAIL '. ' and ' ' = BLANK
* If we're not at the end of the line
* if the cell is not identified, give a message
* otherwise, id the cell is within the array, set the cell state
* move on the the next position in the array
* If the circuit is too wide for the array, give a warning
* return the new Y position
*/
static short AddLineToGrid(Grid,Y,Ox,Oy,blank)
UBYTE Grid[];
short Ox,Oy;
short Y;
int blank;
{
short X = Ox;
short i = X + (Y + Oy) * MAXGRIDW;
int NotDone = TRUE;
UBYTE CellType;
LinePos = 0;
if (Y + Oy >= MAXGRIDH)
{
if (Y + Oy == MAXGRIDH)
ReadError("Circuit Exceeds Maximum Height of %d",MAXGRIDH);
} else {
while (NotDone)
{
CellType = CELL_UNKNOWN;
switch(Line[LinePos++])
{
case '\n':
case '\0':
NotDone = FALSE;
break;
case '[':
if (Line[LinePos++] == ']') CellType = WIRE;
break;
case '#':
if (Line[LinePos++] == '#') CellType = HEAD;
break;
case '=':
if (Line[LinePos++] == '=') CellType = TAIL;
break;
case ' ':
if (Line[LinePos++] == ' ') CellType = blank;
break;
case '.':
if (Line[LinePos++] == ' ') CellType = BLANK;
default:
LinePos++;
break;
}
if (NotDone)
{
if (CellType == CELL_UNKNOWN)
ReadError("Unknown Cell Symbol '%c%c'",
Line[LinePos-2],Line[LinePos-1]);
else if (i < ARRAYSIZE)
Grid[i] = CellType;
i++; X++;
}
}
if (X >= MAXGRIDW && TooWide == FALSE)
{
ReadError("Circuit Exceeds Maximum Width of %d",MAXGRIDW);
TooWide = TRUE;
}
}
return((short)(Y+1));
}
/*
* LoadLibrary()
*
* If the library is being loaded as the Parts List
* put up the proper message and use the proper library strucutre
* Otherwise put up the standard load library message
* Clear the flags
* While there is more to the library
* Read the next line from the file and the next command
* If the command is other than another row of cells or a blank line
* Finish any part in progress and clear the part flags
* Do the correct command:
*
* LIBRARY:
* give an error
*
* PART:
* if the part has a name
* clear the scratch grid and set the flags for starting a part
* copy the name of the part into the part name string
* otherwise warn the user that the part needs a name
*
* CIRCUIT, CELLSIZE, ORIGIN, VIEW, INCLUDE:
* these are legal only in Circuit files, so give an error
*
* CELLS:
* if a part is being made, add the line to the parts' grid
* otherwise, if we haven't already warned the user, do so
*
* BLANKLINE:
* increment the position in the grid
*
* UNKNOWN
* warn the user about the bad command in the file
*/
static void LoadLibraryParts(theLibrary)
LIBRARY *theLibrary;
{
int Command;
short Y = 0;
UBYTE *Grid = NULL;
char PartName[MAXPNAME];
int MakingPart;
int NoMessage = TRUE;
if (theLibrary->Flags & PL_ASPARTS)
{
DoInfoMessage("Loading Library '%s' as Parts",theLibrary->Name);
theLibrary = &PartsList;
} else {
DoInfoMessage("Loading Library '%s'",theLibrary->Name);
}
MakingPart = FALSE; TooWide = FALSE;
while (!EndOfFile)
{
ReadNextLine(); Command = GetNextCommand();
if (Command != COM_CELLS && Command != COM_BLANKLINE)
{
if (MakingPart) FinishPart(PartName,theLibrary);
if (Grid && Y) Grid = NULL; Y = 0;
MakingPart = 0;
}
switch(Command)
{
case COM_LIBRARY:
ReadError("LIBRARY Command must be First in the File");
break;
case COM_PART:
if (Parameters[0])
{
ZeroGrid(NewGen); Y = 0; Grid = NewGen;
NoMessage = TRUE; MakingPart = TRUE; TooWide = FALSE;
strcpy(PartName,Parameters);
} else {
ReadError("Missing Name in PART command");
}
break;
case COM_CIRCUIT:
case COM_CELLSIZE:
case COM_ORIGIN:
case COM_VIEW:
case COM_INCLUDE:
ReadError("Command '%s' Invalid in LIBRARY",NextWord);
break;
case COM_CELLS:
if (Grid)
{
Y = AddLineToGrid(Grid,Y,0,0,0);
} else if (NoMessage) {
ReadError("Missing PART Command?");
NoMessage = FALSE;
}
break;
case COM_BLANKLINE:
if (Y) Y++;
break;
case COM_UNKNOWN:
*(Parameters-1) = 0;
ReadError("Unknown Command '%s'",NextWord);
break;
}
}
}
/*
* IncludeLibrary()
*
* Get a new Library structure, if possible, and link it into the
* list of libraries to load
*/
static void IncludeLibrary(Name)
char *Name;
{
LIBRARY *theLibrary;
extern LIBRARY *NewLibrary();
theLibrary = NewLibrary(Name);
if (theLibrary)
{
theLibrary->Next = LibList;
LibList = theLibrary;
}
}
/*
* LoadLibList()
*
* For each library in the list
* Find the location of the library (sorted) into the library list
* If it does not already appear
* if we can't open the specified library
* inform the user of the problem
* free the library structure
* otherwise
* clear the error flags and read the first line of the library
* find the first command in the library
* if the first command is a LIBRARY command
* load the library's parts and close the file
* warn the user if any errors occur
* otherwise
* close the file and warn the user it is not a library
* If the library has parts in it
* link the library into the list
* otherwise
* free the library structure
* otherwise (already loaded) warn the user about it and free the duplicate
* restore the error status
*/
static void LoadLibList()
{
LIBRARY *theLibrary;
LIBRARY *PrevLib;
LIBRARY **LibPtr;
int OldNoError = noError;
int Command;
char ErrorName[MAXFNAME];
while (theLibrary = LibList)
{
LibList = LibList->Next;
FindListPos(theLibrary->Name,&(FirstLibrary),&PrevLib,&LibPtr);
if (LibPtr)
{
if ((theFile = fopen(theLibrary->Name,"r")) == NULL)
{
sprintf(ErrorName,"Library '%s'",theLibrary->Name);
DosError("Open",ErrorName);
DoInfoMessage(LOADMESSAGE);
FreeLibrary(theLibrary);
} else {
noError = TRUE; EndOfFile = FALSE; ReadNextLine();
while ((Command = GetNextCommand()) == COM_BLANKLINE)
ReadNextLine();
if (Command == COM_LIBRARY)
{
LoadLibraryParts(theLibrary);
fclose(theFile);
if (noError == FALSE)
ReadError("Library '%s' Not Completely Read",theLibrary->Name);
} else {
fclose(theFile);
ReadError("File '%s' is not a Library",theLibrary->Name);
}
if (theLibrary->FirstPart)
{
theLibrary->Prev = PrevLib;
theLibrary->Next = *LibPtr;
if (theLibrary->Next) theLibrary->Next->Prev = theLibrary;
*LibPtr = theLibrary;
Changed = TRUE;
} else {
FreeLibrary(theLibrary);
}
}
} else {
ReadError("Library '%s' Already Loaded",theLibrary->Name);
FreeLibrary(theLibrary);
}
}
noError = OldNoError;
}
/*
* LoadCircuit()
*
* Clear the flags
* While there is more data in the file
* read the next line and get the command on that line
* if the command is not more cells or blank lines
* fininsh making a part, if one is being read
* clear the part flags
* Do the proper command:
*
* LIBRARY, CIRCUIT:
* warn the user about bad LIBRARY and CIRCUIT commands
*
* PART:
* if the part has a name
* clear the scratch grid and set the flags for making a part
* copy the part name into the name string
* otherwise warn about the missing name
*
* CELLSIZE:
* get the specified cell size and check it for errors
*
* ORIGIN:
* get the position of the origin and check for errors
*
* VIEW:
* get the position of the visible grid area and check for errors
*
* INCLUDE:
* include the specified library in the libraries to be loaded
*
* CELLS:
* if there is a part in progress, add the line to it,
* otherwise warn about the missing command
*
* BLANK LINE:
* move on to the next row in the grid
*
* UNKNOWN:
* warn the user about bad commands
* close the circuit file
* load any libraries that were indicated in the file
* if there were errors in the circuit file, let the user know
* clear the info message
*/
static void LoadCircuit(Name)
char *Name;
{
int Command;
short Y = 0;
UBYTE *Grid = CurGen;
char PartName[MAXPNAME];
int tmp1,tmp2;
short Ox = 0, Oy = 0;
int blank = BLANK;
int NoMessage = TRUE;
int MakingPart;
MakingPart = FALSE; TooWide = FALSE;
GridX = GridY = 0;
while (!EndOfFile)
{
ReadNextLine();
Command = GetNextCommand();
if (Command != COM_CELLS && Command != COM_BLANKLINE)
{
if (MakingPart) FinishPart(PartName,&PartsList);
if (Grid && Y) Grid = NULL; Y = 0;
MakingPart = 0;
}
switch(Command)
{
case COM_LIBRARY:
ReadError("Command 'LIBRARY' Invalid in CIRCUIT");
break;
case COM_CIRCUIT:
ReadError("CIRCUIT Command must be First in the File");
break;
case COM_PART:
if (Parameters[0])
{
ZeroGrid(NewGen);
Ox = 0; Oy = 0; blank = 0; NoMessage = TRUE;
Grid = NewGen; Y = 0; MakingPart = TRUE;
strcpy(PartName,Parameters);
} else {
ReadError("Missing Name in PART command");
}
break;
case COM_CELLSIZE:
if (sscanf(Parameters,"%ld",&tmp1) != 1)
ReadError("Invalid CELLSIZE Value");
else if (tmp1 < MINCELLSIZE || tmp1 > MAXCELLSIZE)
ReadError("Invalid CELLSIZE: %d",tmp1);
else CellSize = tmp1;
break;
case COM_ORIGIN:
if (sscanf(Parameters,"%d %d",&tmp1,&tmp2) != 2)
{
ReadError("Invalid ORIGIN Values");
} else if (tmp1 < 0 || tmp1 >= MAXGRIDW ||
tmp2 < 0 || tmp2 >= MAXGRIDH) {
ReadError("ORIGIN Value Out of Range: (%d,%d)",tmp1,tmp2);
} else {
Ox = tmp1; Oy = tmp2;
}
break;
case COM_VIEW:
if (sscanf(Parameters,"%d %d",&tmp1,&tmp2) != 2)
{
ReadError("Invalid VIEW Values");
} else if (tmp1 < 0 || tmp2 >= MAXGRIDW ||
tmp2 < 0 || tmp2 >= MAXGRIDH) {
ReadError("VIEW Value Out of Range: (%d,%d)",tmp1,tmp2);
} else {
GridX = tmp1; GridY = tmp2;
}
break;
case COM_INCLUDE:
IncludeLibrary(Parameters);
break;
case COM_CELLS:
if (Grid)
{
Y = AddLineToGrid(Grid,Y,Ox,Oy,blank);
} else if (NoMessage) {
ReadError("Missing PART Command?");
NoMessage = FALSE;
}
break;
case COM_BLANKLINE:
if (Y) Y++;
break;
case COM_UNKNOWN:
*(Parameters-1) = 0;
ReadError("Unknown Command '%s'",NextWord);
break;
}
}
fclose(theFile);
LoadLibList();
if (noError == FALSE) DoError("Circuit '%s' Not Completely Read",Name);
ClearInfoMessage();
}
/*
* ReadCircuit()
*
* If the previous circuit has been changed, ask if the user wants
* to save the changes
*
* If not (or no changes were made)
* put up the message saying that a file is being read
* clear the grid and the library lists
* load the new circuit
* copy the circuit's grid to the UNDO array and the RESET array
* set the cell size to the new cell size and move to the specified
* viewing position
* clear and refresh the screen and set the sliders
*/
static void ReadCircuit(Name)
char *Name;
{
short tmp1,tmp2;
if (Changed) noError =
DoQuestion(" Changes have not been Saved. Discard Changes? ");
if (noError)
{
DoInfoMessage("Loading Circuit '%s'",Name);
ClearGrid(CurGen);
ClearLibs(); LibList = NULL;
LoadCircuit(Name);
CopyGrid(CurGen,UndoArray);
CopyGrid(CurGen,ResetArray);
tmp1 = GridX; tmp2 = GridY; GridW = GridH = 0;
SetBoardSize(CellSize);
GridX = tmp1; GridY = tmp2;
SetBoardSize(CellSize);
ClearScreen(FALSE);
RefreshScreen(CurGen);
UpdateSliders();
}
}
/*
* ReadLibrary()
*
* close the current file
* if loading a library as parts and changes have been made,
* ask if the user wants to save the changes
* if not
* if loading as parts clear the library list
* set the list of libraries to read to the single specified library
* if loading as parts, indicate this in the library
* load the list
* clear the info message
*/
static void ReadLibrary(Name,Check)
char *Name;
int Check;
{
int OKtoLoad = TRUE;
fclose(theFile);
if (Check && Changed) OKtoLoad =
DoQuestion(" Changes have not been Saved. Discard Changes? ");
if (OKtoLoad)
{
if (Check) ClearLibs();
LibList = NULL;
IncludeLibrary(Name);
if (LibList && Check) LibList->Flags |= PL_ASPARTS;
LoadLibList();
ClearInfoMessage();
}
}
/*
* ReadFile()
*
* If we can open the specified file
* clear the error flags and read the first line
* get the first command in the file
* if the command is CIRCUIT:
* read the circuit
* if it read in OK, set the circuit's name and mark it as unchanged
* If the command is LIBRARY:
* read the library
* if no error and loading as parts, clear the circuit name
* If any other command:
* indicate that the file must have a CIRCUIT or LIBRARY command
* close the file
* otherwise indicate the problem with openning the file
* clear the info message and put the LOAD button back to normal
*/
void ReadFile(Name,Check)
char *Name;
int Check;
{
int Command;
if (theFile = fopen(Name,"r"))
{
noError = TRUE; EndOfFile = FALSE; ReadNextLine();
while ((Command = GetNextCommand()) == COM_BLANKLINE) ReadNextLine();
switch(Command)
{
case COM_CIRCUIT:
ReadCircuit(Name);
if (noError)
{
strcpy(FileName,Name);
Changed = FALSE;
}
break;
case COM_LIBRARY:
ReadLibrary(Name,Check);
if (Check && noError) FileName[0] = 0;
break;
default:
DoError("File must begin with a LIBRARY or CIRCUIT command");
fclose(theFile);
break;
}
} else {
DosError("Read","File");
}
ClearInfoMessage();
InvertGadget(&cGadget[ID_LOAD]);
}