/** \file paramfile.c * Handling of parameter files */ /* XTrackkCad - Model Railroad CAD * Copyright (C) 2019 Martin Fischer * * This program 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 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common.h" #include "compound.h" #include "ctrain.h" #include "custom.h" #include "fileio.h" #include "layout.h" #include "misc2.h" #include "paths.h" #include "include/paramfile.h" #include "include/paramfilelist.h" #include "include/utf8convert.h" #include "common-ui.h" static long paramCheckSum; typedef enum paramFileState(*GetCompatibilityFunction)(int index, SCALEINX_T scale); GetCompatibilityFunction GetCompatibility[] = { GetTrackCompatibility, GetStructureCompatibility, GetCarProtoCompatibility, GetCarPartCompatibility }; #define COMPATIBILITYCHECKSCOUNT COUNT(GetCompatibility) /** * Check whether parameter file is still loaded * * \param fileInx * \return TRUE if loaded, FALSE otherwise */ wBool_t IsParamValid( int fileInx) { if (fileInx == PARAM_DEMO) { return (curDemo >= 0); } else if (fileInx == PARAM_CUSTOM) { return TRUE; } else if (fileInx == PARAM_LAYOUT) { return TRUE; } else if (fileInx >= 0 && fileInx < paramFileInfo_da.cnt) { return (!paramFileInfo(fileInx).deleted); } else { return FALSE; } } char *GetParamFileDir(void) { return (GetCurrentPath(PARAMETERPATHKEY)); } void SetParamFileDir(char *fullPath) { SetCurrentPath(PARAMETERPATHKEY, fullPath); } char * GetParamFileName( int fileInx) { return paramFileInfo(fileInx).name; } char * GetParamFileContents( int fileInx) { return paramFileInfo(fileInx).contents; } bool IsParamFileDeleted(int inx) { return paramFileInfo(inx).deleted; } bool IsParamFileFavorite(int inx) { return paramFileInfo(inx).favorite; } void SetParamFileDeleted(int fileInx, bool deleted) { paramFileInfo(fileInx).deleted = deleted; } void SetParamFileFavorite(int fileInx, bool favorite) { paramFileInfo(fileInx).favorite = favorite; } void ParamCheckSumLine(char * line) { long mult = 1; while (*line) { paramCheckSum += (((long)(*line++)) & 0xFF)*(mult++); } } /** * Set the compatibility state of a parameter file * * \param index parameter file number in list * \return */ void SetParamFileState(int index) { enum paramFileState state = PARAMFILE_NOTUSABLE; enum paramFileState newState; SCALEINX_T scale = GetLayoutCurScale(); //Set yet? if (scale>=0) { for (int i = 0; i < COMPATIBILITYCHECKSCOUNT && state < PARAMFILE_FIT && state != PARAMFILE_UNLOADED; i++) { newState = (*GetCompatibility[i])(index, scale); if (newState > state || newState == PARAMFILE_UNLOADED) { state = newState; } } } paramFileInfo(index).trackState = state; } /** * Check whether file exists and is readable * * \param file The file. * * \returns True if it succeeds, false if it fails. */ static bool CheckFileReadable(const char *file) { if(!access( file, R_OK )) { return TRUE; } else { return FALSE; } } /** * Read a single parameter file and update the parameter file list * * \param fileName full path for parameter file * \return */ int ReadParamFile(const char *fileName) { if (!CheckFileReadable(fileName)) { return(-1); } else { DYNARR_APPEND(paramFileInfo_t, paramFileInfo_da, 10); curParamFileIndex = paramFileInfo_da.cnt - 1; paramFileInfo(curParamFileIndex).name = MyStrdup(fileName); paramFileInfo(curParamFileIndex).valid = TRUE; paramFileInfo(curParamFileIndex).deleted = !ReadParams(0, NULL, fileName); paramFileInfo(curParamFileIndex).contents = MyStrdup(curContents); SetParamFileState(curParamFileIndex); } return (curParamFileIndex); } /** * Reload a single parameter file that had been unloaded before. * * \param fileindex index of previously created paramFileInfo * * \returns */ int ReloadDeletedParamFile(int fileindex) { curParamFileIndex = fileindex; paramFileInfo(curParamFileIndex).valid = TRUE; paramFileInfo(curParamFileIndex).deleted = !ReadParams(0, NULL, paramFileInfo(curParamFileIndex).name); paramFileInfo(curParamFileIndex).contents = MyStrdup(curContents); SetParamFileState(curParamFileIndex); return (curParamFileIndex); } /** * Parameter file reader and interpreter * * \param key unused * \param dirName prefix for parameter file path * \param fileName name of parameter file * \return */ bool ReadParams( long key, const char * dirName, const char * fileName) { FILE * oldFile; char *cp; wIndex_t oldLineNum; wIndex_t pc; long oldCheckSum; long checkSum = 0; BOOL_T checkSummed; paramVersion = -1; if (dirName) { MakeFullpath(¶mFileName, dirName, fileName, NULL); } else { MakeFullpath(¶mFileName, fileName, NULL); } paramLineNum = 0; curBarScale = -1; curContents = MyStrdup(fileName); curSubContents = curContents; //LOG1( log_paramFile, ("ReadParam( %s )\n", fileName ) ) SetCLocale(); paramFile = fopen(paramFileName, "r"); if (paramFile == NULL) { /* Reset the locale settings */ SetUserLocale(); NoticeMessage(MSG_OPEN_FAIL, _("Continue"), NULL, _("Parameter"), paramFileName, strerror(errno)); return FALSE; } paramCheckSum = key; paramLineNum = 0; checkSummed = FALSE; BOOL_T skip = false; int skipLines = 0; while (paramFile && (fgets(paramLine, 1024, paramFile)) != NULL) { paramLineNum++; Stripcr(paramLine); if (strncmp(paramLine, "CHECKSUM ", 9) == 0) { checkSum = atol(paramLine + 9); checkSummed = TRUE; goto nextLine; } ParamCheckSumLine(paramLine); if (paramLine[0] == '#') { /* comment */ } else if (paramLine[0] == 0) { /* empty paramLine */ } else if (strncmp(paramLine, "INCLUDE ", 8) == 0) { cp = ¶mLine[8]; while (*cp && isspace((unsigned char)*cp)) { cp++; } if (!*cp) { InputError("INCLUDE - no file name", TRUE); /* Close file and reset the locale settings */ if (paramFile) { fclose(paramFile); } SetUserLocale(); return FALSE; } oldFile = paramFile; oldLineNum = paramLineNum; oldCheckSum = paramCheckSum; if (!ReadParams(key, dirName, cp)) { SetUserLocale(); return FALSE; } paramFile = oldFile; paramLineNum = oldLineNum; paramCheckSum = oldCheckSum; if (dirName) { MakeFullpath(¶mFileName, dirName, fileName, NULL); } else { MakeFullpath(¶mFileName, fileName); } skip = FALSE; } else if (strncmp(paramLine, "CONTENTS ", 9) == 0) { #ifdef UTFCONVERT ConvertUTF8ToSystem(paramLine + 9); #endif curContents = MyStrdup(paramLine + 9); curSubContents = curContents; skip = FALSE; } else if (strncmp(paramLine, "SUBCONTENTS ", 12) == 0) { #ifdef UTFCONVERT ConvertUTF8ToSystem(paramLine + 12); #endif // UTFCONVERT curSubContents = MyStrdup(paramLine + 12); skip = FALSE; } else if (strncmp(paramLine, "PARAM ", 6) == 0) { paramVersion = strtol(paramLine + 6, &cp, 10); if (cp) while (*cp && isspace((unsigned char)*cp)) cp++; if (paramVersion > iParamVersion) { if (cp && *cp) { NoticeMessage(MSG_PARAM_UPGRADE_VERSION1, _("Ok"), NULL, paramVersion, iParamVersion, sProdName, cp); } else { NoticeMessage(MSG_PARAM_UPGRADE_VERSION2, _("Ok"), NULL, paramVersion, iParamVersion, sProdName); } break; } if (paramVersion < iMinParamVersion) { NoticeMessage(MSG_PARAM_BAD_FILE_VERSION, _("Ok"), NULL, paramVersion, iMinParamVersion, sProdName); break; } } else if (skip && (strncmp(paramLine, " ", 1) == 0)) { //Always skip to next line starting in LeftHandColumn skipLines++; goto nextLine; } else { for (pc = 0; pc < paramProc_da.cnt; pc++) { if (strncmp(paramLine, paramProc(pc).name, strlen(paramProc(pc).name)) == 0) { skip = FALSE; //Stop skip so we re-message paramProc(pc).proc(paramLine); goto nextLine; } } if (!skip) { if (InputError(_("Unknown param file line - skip until next good object?"), TRUE)) { //OK to carry on /* SKIP until next main line we recognize */ skip = TRUE; skipLines++; goto nextLine; } else { if (skipLines > 0) NoticeMessage(MSG_PARAM_LINES_SKIPPED, _("Ok"), NULL, paramFileName, skipLines); if (paramFile) { fclose(paramFile); paramFile = NULL; } if (paramFileName) { free(paramFileName); paramFileName = NULL; } SetUserLocale(); return FALSE; } } skipLines++; } nextLine: ; } if (key) { if (!checkSummed || checkSum != paramCheckSum) { /* Close file and reset the locale settings */ if (paramFile) { fclose(paramFile); } SetUserLocale(); NoticeMessage(MSG_PROG_CORRUPTED, _("Ok"), NULL, paramFileName); return FALSE; } } if (skipLines > 0) NoticeMessage(MSG_PARAM_LINES_SKIPPED, _("Ok"), NULL, paramFileName, skipLines); if (paramFile) { fclose(paramFile); } free(paramFileName); paramFileName = NULL; SetUserLocale(); return TRUE; }