/****************************************************************************** Copyright (c) 2000-2001 Richard Woolcock Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ******************************************************************************/ /****************************************************************************** File Name : file_io.c ****************************************************************************** Description : Contains the file loading and saving functions. ******************************************************************************/ /****************************************************************************** Required library files. ******************************************************************************/ #include <stdlib.h> #include <stdio.h> #include <stdarg.h> #include <string.h> #include <unistd.h> #include <ctype.h> #include <netinet/in.h> #include <sys/time.h> #include <sys/types.h> #include <sys/socket.h> #include "sockets.h" #include "string.h" #include "glad.h" #include "text_io.h" /****************************************************************************** Required types. ******************************************************************************/ typedef enum { TEnum, /* Enumerated type */ TAlpha, /* Alphabetical string (letters only) */ TString, /* Normal string (can contain any characters) */ TInt, /* Integer */ TEnd /* Indicates the end of the player file */ } TType_t; typedef struct { char * szVariable; char * szTextValue; int iNumberValue; } enum_t; typedef struct { char * szName; void * pvStorage; TType_t eType; int iMin; int iMax; int iDefault; } playerfile_t; /****************************************************************************** Required literals. ******************************************************************************/ #define MAX_PFILE_LINE 256 /* Max size of a single line read from pfile */ #define MAX_TEXT_SIZE 4096 /* Max size of a single textual description */ /****************************************************************************** Required macros. ******************************************************************************/ #define PFILE(p) (void*) &(s_stBody.p) /****************************************************************************** Local operation prototypes. ******************************************************************************/ static bool ReadEntry( char *szName, char *szValue ); /****************************************************************************** Local variables. ******************************************************************************/ static body_t s_stBody; /* Storage variable used for saving/loading */ const playerfile_t a_kstPlayerFileTable[] = { /* Keyword Storage variable Type Minimum Maximum Default */ { "Name", PFILE(a_chName), TAlpha, 1, 15, -1 }, { "Password", PFILE(a_chPassword), TString, 1, 15, -1 }, { "Status", PFILE(eStatus), TEnum, CITIZEN, CHALLENGER, GLADIATOR }, { "Strength", PFILE(iStats[STR]), TInt, 1, 9, 1 }, { "Dexterity",PFILE(iStats[DEX]), TInt, 1, 9, 1 }, { "Stamina", PFILE(iStats[STA]), TInt, 1, 9, 1 }, { "Size", PFILE(iStats[SIZ]), TInt, 1, 9, 1 }, { "Wits", PFILE(iStats[WIT]), TInt, 1, 9, 1 }, { "Sex", PFILE(eSex), TEnum, MALE, FEMALE, MALE }, { "Room", PFILE(iRoom), TInt, 0, 100000, 0 }, { "Wins", PFILE(iWin), TInt, 0, 100000, 0 }, { "Losses", PFILE(iLoss), TInt, 0, 100000, 0 }, { "Kills", PFILE(iKill), TInt, 0, 100000, 0 }, { "Wounds", PFILE(iDam), TInt, 0, 99, 0 }, /* Anything after "End" will be saved but not loaded */ { "End", NULL, TEnd, 0, 0, 0 }, { NULL, NULL, 0, 0, 0, 0 } /* This must ALWAYS be at the end */ }; const enum_t a_kstEnumTable[] = { /* Name String value Numeric value */ { "Status", "CROWD", CROWD }, { "Status", "CITIZEN", CITIZEN }, { "Status", "TRAINING", TRAINING }, { "Status", "GLADIATOR", GLADIATOR }, { "Status", "CHALLENGER", CHALLENGER }, { "Status", "FIGHTING", FIGHTING }, { "Sex", "MALE", MALE }, { "Sex", "FEMALE", FEMALE }, { NULL, NULL, 0 } /* This must ALWAYS be at the end */ }; /****************************************************************************** Required global operations. ******************************************************************************/ /* Function: SaveBody * * This function saves the body which is passed in as an argument. * * The function takes one parameter, as follows: * * pstBody: The body to be saved. * * This function returns a bool, which is TRUE if the body was successfully * saved and FALSE if it was not. */ bool SaveBody( body_t *pstBody ) { FILE* pFile; /* File pointer */ int i = 0; /* Loop counter */ char a_chFileName[64]; /* Name of the player file */ bool bEnd = FALSE; /* Determine the file name */ sprintf( a_chFileName, "../player/%s", pstBody->a_chName ); /* Try and open the file */ if ( ( pFile = fopen(a_chFileName,"w") ) == NULL ) { Log( "SaveBody: Unable to open player file '%s'\n", a_chFileName ); return ( FALSE ); } /* Copy the body */ s_stBody = *pstBody; /* Write out the player file */ while ( a_kstPlayerFileTable[i].szName != NULL ) { int j = 0; /* Loop counter */ bool bFound = FALSE; switch ( a_kstPlayerFileTable[i].eType ) { case TEnum: while ( a_kstEnumTable[j].szVariable != NULL && !bFound ) { if ( !strcmp( a_kstEnumTable[j].szVariable, a_kstPlayerFileTable[i].szName ) ) { if ( a_kstEnumTable[j].iNumberValue == *((int*)a_kstPlayerFileTable[i].pvStorage) ) { fprintf( pFile, "%-10s %s\n", a_kstPlayerFileTable[i].szName, a_kstEnumTable[j].szTextValue ); bFound = TRUE; } } j++; } break; case TAlpha: fprintf( pFile, "%-10s %s\n", a_kstPlayerFileTable[i].szName, (char*) a_kstPlayerFileTable[i].pvStorage ); break; case TString: fprintf( pFile, "%-10s %s\n", a_kstPlayerFileTable[i].szName, (char*) a_kstPlayerFileTable[i].pvStorage ); break; case TInt: fprintf( pFile, "%-10s %d\n", a_kstPlayerFileTable[i].szName, *((int*)a_kstPlayerFileTable[i].pvStorage) ); break; case TEnd: bEnd = TRUE; fprintf( pFile, "End\n" ); break; } i++; } /* Close the player file */ fclose(pFile); /* Make sure it put the "End" on the end, as it is needed for loading */ if ( !bEnd ) { Log( "SaveBody: The end of the player file is missing!\n" ); return ( FALSE ); } return ( TRUE ); } /* Function: LoadBody * * This function loads the body which is passed in as an argument. * * The function takes two parameters, as follows: * * szFileName: The file name of the player file. * pstBody: The body to be loaded. * * This function returns a bool, which is TRUE if the body was successfully * loaded and FALSE if it was not. */ bool LoadBody( char *szFileName, body_t *pstBody ) { FILE* pFile; /* File pointer */ int iLetter; /* Used for reading from the player file */ char a_chFileName[64]; /* Name of the player file */ bool bEnd = FALSE; /* End of file */ char a_chReadLine[MAX_PFILE_LINE]; /* Line read from the player file */ int iSize = 0; /* Size of the line */ /* Determine the file name */ sprintf( a_chFileName, "../player/%s", szFileName ); /* Try and open the file */ if ( ( pFile = fopen(a_chFileName,"r") ) == NULL ) { /* If you can't find the player file, return */ return ( FALSE ); } /* This will be wiped over if their player file is actually okay */ pstBody->eStatus = CORRUPTED_PFILE; /* Copy the body so that pointers/etc aren't lost */ s_stBody = *pstBody; /* Read in the player file */ while ( !bEnd ) { iLetter = getc(pFile); switch ( iLetter ) { case EOF: /* Log the fault, close the player file and return */ Log( "LoadBody: Player file '%s' is corrupted (reached EOF).\n", szFileName ); fclose(pFile); return ( FALSE ); default: if ( iSize+2 >= MAX_PFILE_LINE ) { /* Log the fault, close the player file and return */ Log( "LoadBody: Player file '%s' is corrupted (line too long).\n", szFileName ); fclose(pFile); return ( FALSE ); } else /* There is enough space for the next character plus a NUL */ { a_chReadLine[iSize++] = iLetter; } break; case '\n': if ( iSize+1 >= MAX_PFILE_LINE ) { /* Log the fault, close the player file and return */ Log( "LoadBody: Player file '%s' is corrupted (line too long).\n", szFileName ); fclose(pFile); return ( FALSE ); } else /* There is enough space to stick a NUL on the end */ { char *szVariable; char *szValue; char *szInput = a_chReadLine; /* Terminate the input string */ a_chReadLine[iSize] = '\0'; iSize = 0; szVariable = ChopToken( &szInput ); szValue = ChopToken( &szInput ); if ( szVariable[0] == '\0' || szValue[0] == '\0' || ReadEntry( szVariable, szValue ) == FALSE ) { if ( !strcmp( szVariable, "End" ) ) { bEnd = TRUE; } else { /* Log the fault, close the player file and return */ Log( "LoadBody: Player file '%s' is corrupted.\n", szFileName ); fclose(pFile); return ( FALSE ); } } } break; } } /* Copy the body back over the player's */ *pstBody = s_stBody; /* Close the player file */ fclose(pFile); return ( TRUE ); } /* Function: LoadHelp * * This function loads the help file which is passed in as an argument. * * The function takes one parameters, as follows: * * szFileName: The file name of the help file. * * This function returns a pointer to the help file, which is stored on the * heap. */ char *LoadHelp( char *szFileName ) { FILE* pFile; /* File pointer */ int iLetter; /* Used for reading from the help file */ char a_chFileName[128]; /* Name of the help file */ char a_chReadLine[MAX_TEXT_SIZE]; /* Line read from the help file on disk */ int iSize = 0; /* Size of the line */ if ( !StringAlpha(szFileName) ) { /* If the helpfile has an invalid filename, return NULL */ Log( "LoadHelp: Help file '%s' is not a valid filename.\n\r", szFileName ); return ( NULL ); } /* Determine the file name */ sprintf( a_chFileName, "../help/%s.txt", szFileName ); /* Try and open the file */ if ( ( pFile = fopen(a_chFileName,"r") ) == NULL ) { /* If you can't find the help file, return NULL */ return ( NULL ); } /* Read in the first character of the help file */ iLetter = getc(pFile); /* Read in the rest of the help file */ while ( iLetter != EOF ) { if ( iSize+2 >= MAX_TEXT_SIZE ) { /* Log the fault, close the help file and return */ Log( "LoadHelp: Help file '%s' exceeds maximum size of %d characters.\n", szFileName, MAX_TEXT_SIZE ); fclose(pFile); return ( NULL ); } else /* There is enough space for the next character plus a NUL */ { /* Only store printable characters */ a_chReadLine[iSize++] = iLetter; } iLetter = getc(pFile); /* Read the next character */ } a_chReadLine[iSize] = '\0'; /* Terminate the string */ while ( iSize > 0 && (a_chReadLine[iSize-1] == '\n' || a_chReadLine[iSize-1] == '\r') ) { /* Strip off any blank lines from the end of the help file */ a_chReadLine[--iSize] = '\0'; } /* Close the help file */ fclose(pFile); return ( strdup( a_chReadLine ) ); } /****************************************************************************** Required global operations. ******************************************************************************/ /* Function: ReadEntry * * This function reads through the player file table looking for a match for * the variable passed in. Once found, the variable is stored in the body. * * The function takes two parameters, as follows: * * szName: The name of the variable. * szValue: The value of the variable. * * This function returns a bool, which is TRUE if a match was found and FALSE * if it was not. */ static bool ReadEntry( char *szName, char *szValue ) { int i = 0; /* Loop counter */ /* Write out the player file */ while ( a_kstPlayerFileTable[i].szName != NULL ) { int j = 0; /* Loop counter */ if ( !strcmp( a_kstPlayerFileTable[i].szName, szName ) ) { switch ( a_kstPlayerFileTable[i].eType ) { case TEnum: while ( a_kstEnumTable[j].szVariable != NULL ) { if ( !strcmp( a_kstEnumTable[j].szVariable, szName ) ) { if ( !strcmp( a_kstEnumTable[j].szTextValue, szValue ) ) { *((int*)a_kstPlayerFileTable[i].pvStorage) = a_kstEnumTable[j].iNumberValue; return ( TRUE ); } } j++; } Log( "ReadEntry: Invalid enum value '%s' for %s.\n", szValue, szName ); return ( FALSE ); case TAlpha: if ( !StringAlpha(szValue) ) { Log( "ReadEntry: Variable '%s' contains non-alphabetical character '%c'.\n", szName, szValue[j] ); return ( FALSE ); } /* Intentional fall-through */ case TString: strcpy( (char*)a_kstPlayerFileTable[i].pvStorage, szValue ); if ( strlen(szValue) < a_kstPlayerFileTable[i].iMin ) { Log( "ReadEntry: Variable '%s' below minimum length.\n", szName ); return ( FALSE ); } else if ( strlen(szValue) > a_kstPlayerFileTable[i].iMax ) { Log( "ReadEntry: Variable '%s' above maximum length.\n", szName ); return ( FALSE ); } return ( TRUE ); case TInt: *((int*)a_kstPlayerFileTable[i].pvStorage) = TextCalculate( NULL, szValue ); if ( *((int*)a_kstPlayerFileTable[i].pvStorage) < a_kstPlayerFileTable[i].iMin ) { Log( "ReadEntry: Variable '%s' below minimum valid value.\n", szName ); return ( FALSE ); } else if ( *((int*)a_kstPlayerFileTable[i].pvStorage) > a_kstPlayerFileTable[i].iMax ) { Log( "ReadEntry: Variable '%s' above maximum valid value.\n", szName ); return ( FALSE ); } return ( TRUE ); case TEnd: return ( FALSE ); } } i++; } /* No match was found */ return ( FALSE ); }