/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * 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 3 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, see . * */ #include "common/config-manager.h" #include "common/file.h" #include "bagel/spacebar/boflib/options.h" #include "bagel/boflib/misc.h" #include "bagel/boflib/log.h" #include "bagel/boflib/string_functions.h" namespace Bagel { namespace SpaceBar { COption::COption(const char *pszInit) { _szBuf[0] = '\0'; if (pszInit != nullptr) { assert(strlen(pszInit) < MAX_OPTION_LEN); Common::strcpy_s(_szBuf, pszInit); } } CBofOptions::CBofOptions(const char *pszOptionFile) { _szFileName[0] = '\0'; _pOptionList = nullptr; _bDirty = false; if (pszOptionFile != nullptr) { loadOptionFile(pszOptionFile); } } CBofOptions::~CBofOptions() { assert(isValidObject(this)); release(); _szFileName[0] = '\0'; } ErrorCode CBofOptions::loadOptionFile(const char *pszOptionFile) { assert(isValidObject(this)); assert(pszOptionFile != nullptr); assert(*pszOptionFile != '\0'); assert(strlen(pszOptionFile) < MAX_FNAME); release(); Common::strcpy_s(_szFileName, pszOptionFile); return load(); } ErrorCode CBofOptions::load() { assert(isValidObject(this)); // Assume no error ErrorCode errorCode = ERR_NONE; // Free any previous option info release(); Common::File f; if (Common::File::exists(_szFileName) && f.open(_szFileName)) { char szBuf[MAX_OPTION_LEN]; assert(_pOptionList == nullptr); while (readLine(&f, szBuf)) { COption *pNewOption = new COption(szBuf); if (_pOptionList != nullptr) { _pOptionList->addToTail(pNewOption); } else { _pOptionList = pNewOption; } } if (_pOptionList != nullptr) { // _pOptionList must always be the head of the list! assert(_pOptionList == _pOptionList->getHead()); } f.close(); } else { errorCode = ERR_FOPEN; } return errorCode; } void CBofOptions::release() { assert(isValidObject(this)); commit(); // Release each item in the list while (_pOptionList != nullptr) { COption *pNextItem = (COption *)_pOptionList->getNext(); delete _pOptionList; _pOptionList = pNextItem; } } ErrorCode CBofOptions::commit() { assert(isValidObject(this)); ErrorCode errorCode = ERR_NONE; if ((_pOptionList != nullptr) && _bDirty) { // _pOptionList must always be the head of the list! assert(_pOptionList == _pOptionList->getHead()); warning("TODO: Look into refactoring options to ConfMan if needed"); } return errorCode; } ErrorCode CBofOptions::writeSetting(const char *pszSection, const char *pszVar, const char *pszNewValue) { // Can't access nullptr pointers assert(pszSection != nullptr); assert(pszVar != nullptr); assert(pszNewValue != nullptr); char szValueBuf[MAX_OPTION_LEN]; // Assume no error ErrorCode errorCode = ERR_NONE; // Indicate that the options file needs to be updated _bDirty = true; Common::sprintf_s(szValueBuf, "%s=%s", pszVar, pszNewValue); // Find this option based on it's section COption *pOption = findOption(pszSection, pszVar); if (pOption != nullptr) { // Update option with new value Common::strcpy_s(pOption->_szBuf, szValueBuf); } else { // Did not find option (or possibly also did not find section) // If this section is not in the file COption *pSection = findSection(pszSection); if (pSection == nullptr) { char szSectionBuf[MAX_OPTION_LEN]; // Then create a new section Common::sprintf_s(szSectionBuf, "[%s]", pszSection); pSection = new COption(szSectionBuf); if (_pOptionList != nullptr) { _pOptionList->addToTail(pSection); } else { _pOptionList = pSection; } } // Add this option to the specified section pOption = new COption(szValueBuf); pSection->Insert(pOption); } return errorCode; } ErrorCode CBofOptions::writeSetting(const char *pszSection, const char *pszVar, int nNewValue) { // Can't access nullptr pointers assert(pszSection != nullptr); assert(pszVar != nullptr); char szBuf[20]; Common::sprintf_s(szBuf, "%d", nNewValue); ErrorCode errorCode = writeSetting(pszSection, pszVar, szBuf); return errorCode; } ErrorCode CBofOptions::readSetting(const char *section, const char *option, char *stringValue, const char *defaultValue, uint32 maxLen) { // Can't access nullptr pointers assert(section != nullptr); assert(option != nullptr); assert(stringValue != nullptr); assert(defaultValue != nullptr); // If ConfMan has a key of a given name, no matter the section, // than it takes precedence over any INI file value if (ConfMan.hasKey(option)) { Common::String str = ConfMan.get(option); Common::strcpy_s(stringValue, maxLen, str.c_str()); return ERR_NONE; } char szBuf[MAX_OPTION_LEN]; // Assume no error ErrorCode errorCode = ERR_NONE; // Assume we will need to use the default setting Common::strcpy_s(stringValue, maxLen, defaultValue); // Try to find this option COption *pOption = findOption(section, option); if (pOption != nullptr) { assert(strlen(pOption->_szBuf) < MAX_OPTION_LEN); Common::strcpy_s(szBuf, pOption->_szBuf); // Strip out any comments strreplaceChar(szBuf, ';', '\0'); // Find 1st equal sign char *p = strchr(szBuf, '='); // Error in .INI file if we can't find the equal sign if (p != nullptr) { p++; if (strlen(p) > 0) Common::strcpy_s(stringValue, maxLen, p); } else { logError(buildString("Error in %s, section: %s, entry: %s", _szFileName, section, option)); errorCode = ERR_FTYPE; } } return errorCode; } ErrorCode CBofOptions::readSetting(const char *section, const char *option, int *intValue, int defaultValue) { assert(section != nullptr); assert(option != nullptr); assert(intValue != nullptr); // If ConfMan has a key of a given name, no matter the section, // than it takes precedence over any INI file value if (ConfMan.hasKey(option)) { *intValue = ConfMan.getInt(option); return ERR_NONE; } char szDefault[20], szBuf[20]; Common::sprintf_s(szDefault, "%d", defaultValue); ErrorCode errorCode = readSetting(section, option, szBuf, szDefault, 20); *intValue = atoi(szBuf); return errorCode; } ErrorCode CBofOptions::readSetting(const char *section, const char *option, bool *boolValue, bool defaultValue) { assert(section != nullptr); assert(option != nullptr); assert(boolValue != nullptr); // If ConfMan has a key of a given name, no matter the section, // than it takes precedence over any INI file value if (ConfMan.hasKey(option)) { *boolValue = ConfMan.getBool(option); return ERR_NONE; } int v; ErrorCode errorCode = readSetting(section, option, &v, defaultValue); *boolValue = v != 0; return errorCode; } COption *CBofOptions::findSection(const char *pszSection) { assert(isValidObject(this)); assert(pszSection != nullptr); assert(*pszSection != '\0'); char szSectionBuf[MAX_OPTION_LEN]; Common::sprintf_s(szSectionBuf, "[%s]", pszSection); int nLength = strlen(szSectionBuf); COption *pOption = _pOptionList; while (pOption != nullptr) { if (!scumm_strnicmp(pOption->_szBuf, szSectionBuf, nLength)) { break; } pOption = (COption *)pOption->getNext(); } return pOption; } COption *CBofOptions::findOption(const char *pszSection, const char *pszVar) { assert(isValidObject(this)); assert(pszSection != nullptr); assert(pszVar != nullptr); assert(*pszSection != '\0'); assert(*pszVar != '\0'); // Assume we won't find the option COption *pFound = nullptr; int nLength = strlen(pszVar); COption *pStart = findSection(pszSection); if (pStart != nullptr) { COption *pOption = (COption *)pStart->getNext(); while (pOption != nullptr) { if (pOption->_szBuf[0] == '[') { // this option was not found pFound = nullptr; break; } if (!scumm_strnicmp(pOption->_szBuf, pszVar, nLength)) { pFound = pOption; break; } pOption = (COption *)pOption->getNext(); } } return pFound; } bool CBofOptions::readLine(Common::SeekableReadStream *pFile, char *pszBuf) { assert(pFile != nullptr); assert(pszBuf != nullptr); if (pFile->eos()) return false; Common::String line = pFile->readLine(); Common::strcpy_s(pszBuf, 256, line.c_str()); return true; } } // namespace SpaceBar } // namespace Bagel