ResidualVM logo ResidualVM website - Forums - Contact us BuildBot - Doxygen - Wiki curved edge

textsplit.cpp

Go to the documentation of this file.
00001 /* ResidualVM - A 3D game interpreter
00002  *
00003  * ResidualVM is the legal property of its developers, whose names
00004  * are too numerous to list here. Please refer to the AUTHORS
00005  * file distributed with this source distribution.
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020  *
00021  */
00022 
00023 #include "common/util.h"
00024 #include "common/textconsole.h"
00025 #include "common/stream.h"
00026 
00027 #include "engines/grim/textsplit.h"
00028 
00029 namespace Grim {
00030 
00031 static bool isCodeSeparator(char c) {
00032     return (c == ' ' || c == ',' || c == '.' || c == '%' || c == '\'' || c == ':');
00033 }
00034 
00035 static bool isSeparator(char c) {
00036     return (c == ' ' || c == ',' || c == ':');
00037 }
00038 
00039 int power(int base, int exp) {
00040     int res = 1;
00041     for (int i = 0; i < exp; ++i) {
00042         res *= base;
00043     }
00044     return res;
00045 }
00046 
00047 static float str2float(const char *str) {
00048     int len = strlen(str);
00049     int dotpos = len;
00050     char *int_part = new char[len + 1];
00051     int j = 0;
00052     for (int i = 0; i < len; ++i) {
00053         if (str[i] != '.') {
00054             int_part[j++] = str[i];
00055         } else {
00056             dotpos = i;
00057             break;
00058         }
00059     }
00060     int_part[j++] = '\0';
00061 
00062     // Must use double here. float doesn't have enough precision for the sector
00063     // vertices, and the pathfinder may break, like when olivia returns from
00064     // the microphone after reciting a poem.
00065     double num = atoi(int_part);
00066     int sign = (str[0] == '-' ? -1 : 1);
00067     j = 0;
00068     for (int i = dotpos + 1; i < len; ++i) {
00069         double part = (double)(str[i] - 48) / (double)power(10, ++j);
00070         num += part * sign;
00071     }
00072 
00073     delete[] int_part;
00074 
00075     return num;
00076 }
00077 
00078 static bool isNum(char c) {
00079     return (c >= '0' && c <= '9');
00080 }
00081 
00082 static char *parseCharacterClass(const char *code, bool *isNegated) {
00083     uint32 length = strlen(code);
00084 
00085     char *chars = new char[length];
00086     *isNegated = code[1] == '^';
00087     uint32 j = 0;
00088     uint32 k = (*isNegated ? 2 : 1);
00089 
00090     for (; k < length && code[k] != ']'; ++k) {
00091         assert(code[k] != '[' && code[k] != '-');
00092         chars[j++] = code[k];
00093     }
00094     chars[j++] = '\0';
00095 
00096     return chars;
00097 }
00098 
00099 // This function is modelled after sscanf, and supports a subset of its features. See sscanf documentation
00100 // for information about the syntax it accepts.
00101 static void parse(const char *line, const char *fmt, int field_count, va_list va) {
00102     char *str = scumm_strdup(line);
00103     const int len = strlen(str);
00104     for (int i = 0; i < len; ++i) {
00105         if (str[i] == '\t')
00106             str[i] = ' ';
00107     }
00108 
00109     char *format = scumm_strdup(fmt);
00110     const int formatlen = strlen(format);
00111     for (int i = 0; i < formatlen; ++i) {
00112         if (format[i] == '\t')
00113             format[i] = ' ';
00114     }
00115 
00116     int count = 0;
00117     const char *src = str;
00118     const char *end = str + len;
00119     for (int i = 0; i < formatlen; ++i) {
00120         if (format[i] == '%') {
00121             char code[10];
00122             char width[10];
00123             int j = 0;
00124             int jw = 0;
00125             bool inBrackets = false;
00126             while (++i < formatlen && !isCodeSeparator(format[i])) {
00127                 char c = format[i];
00128                 if (c == '[') {
00129                     inBrackets = true;
00130                 } else if (inBrackets && c == ']') {
00131                     inBrackets = false;
00132                 }
00133                 if (!inBrackets && isNum(c)) {
00134                     width[jw++] = c;
00135                 } else {
00136                     code[j++] = c;
00137                 }
00138             }
00139             code[j] = '\0';
00140             width[jw] = '\0';
00141 
00142             void *var = va_arg(va, void *);
00143             if (strcmp(code, "n") == 0) {
00144                 *(int*)var = src - str;
00145                 continue;
00146             }
00147 
00148             char s[2000];
00149 
00150             unsigned int fieldWidth = 1;
00151             if (width[0] != '\0') {
00152                 fieldWidth = atoi(width);
00153             }
00154 
00155             j = 0;
00156             if (code[0] == 'c') {
00157                 for (unsigned int n = 0; n < fieldWidth; ++n) {
00158                     s[j++] = src[0];
00159                     ++src;
00160                 }
00161             } else if (code[0] == '[') {
00162                 bool isNegated;
00163                 char *allowed = parseCharacterClass(code, &isNegated);
00164 
00165                 while (src != end) {
00166                     bool inSet = strchr(allowed, src[0]) != nullptr;
00167                     if ((isNegated && inSet) || (!isNegated && !inSet))
00168                         break;
00169 
00170                     s[j++] = src[0];
00171                     ++src;
00172                 }
00173 
00174                 delete[] allowed;
00175             } else {
00176                 char nextChar = format[i];
00177                 while (src[0] == ' ') { //skip initial whitespace
00178                     ++src;
00179                 }
00180                 while (src != end && src[0] != nextChar && !isSeparator(src[0])) {
00181                     s[j++] = src[0];
00182                     ++src;
00183                 }
00184             }
00185 
00186             s[j] = '\0';
00187             --i;
00188 
00189             if (width[0] == '\0') {
00190                 fieldWidth = strlen(s);
00191             }
00192 
00193             if (strcmp(code, "d") == 0) {
00194                 *(int*)var = atoi(s);
00195             } else if (strcmp(code, "x") == 0) {
00196                 *(int*)var = strtol(s, (char **) nullptr, 16);
00197             } else if (strcmp(code, "f") == 0) {
00198                 *(float*)var = str2float(s);
00199             } else if (strcmp(code, "c") == 0) {
00200                 *(char*)var = s[0];
00201             } else if (strcmp(code, "s") == 0) {
00202                 char *string = (char*)var;
00203                 strncpy(string, s, fieldWidth);
00204                 if (fieldWidth <= strlen(s)) {
00205                     // add terminating \0
00206                     string[fieldWidth] = '\0';
00207                 }
00208             } else if (code[0] == '[') {
00209                 char *string = (char*)var;
00210                 strncpy(string, s, fieldWidth);
00211                 string[fieldWidth - 1] = '\0';
00212             } else {
00213                 error("Code not handled: \"%s\" \"%s\"\n\"%s\" \"%s\"", code, s, line, fmt);
00214             }
00215 
00216             ++count;
00217             continue;
00218         }
00219 
00220         while (src[0] == ' ') {
00221             ++src;
00222         }
00223         if (src == end)
00224             break;
00225 
00226         if (src[0] != format[i] && format[i] != ' ') {
00227             error("Expected line of format '%s', got '%s'", fmt, line);
00228         }
00229 
00230         if (src == end)
00231             break;
00232         if (format[i] != ' ') {
00233             ++src;
00234             if (src == end)
00235                 break;
00236         }
00237     }
00238     free(str);
00239     free(format);
00240 
00241     if (count < field_count) {
00242         error("Expected line of format '%s', got '%s'", fmt, line);
00243     }
00244 }
00245 
00246 
00247 TextSplitter::TextSplitter(const Common::String &fname, Common::SeekableReadStream *data) : _fname(fname) {
00248     char *line;
00249     int i;
00250     uint32 len = data->size();
00251 
00252     _stringData = new char[len + 1];
00253     data->read(_stringData, len);
00254     _stringData[len] = '\0';
00255     // Find out how many lines of text there are
00256     _numLines = _lineIndex = 0;
00257     line = (char *)_stringData;
00258     while (line) {
00259         line = strchr(line, '\n');
00260         if (line) {
00261             _numLines++;
00262             line++;
00263         }
00264     }
00265     // Allocate an array of the lines
00266     _lines = new char *[_numLines];
00267     line = (char *)_stringData;
00268     for (i = 0; i < _numLines; i++) {
00269         char *lastLine = line;
00270         line = strchr(lastLine, '\n');
00271         *line = '\0';
00272         _lines[i] = lastLine;
00273         line++;
00274     }
00275     _currLine = nullptr;
00276     processLine();
00277 }
00278 
00279 TextSplitter::~TextSplitter() {
00280     delete[] _stringData;
00281     delete[] _lines;
00282 }
00283 
00284 bool TextSplitter::checkString(const char *needle) {
00285     // checkString also needs to check for extremely optional
00286     // components like "object_art" which can be missing entirely
00287     if (!getCurrentLine()) {
00288         return false;
00289     } else {
00290         Common::String haystack(getCurrentLine());
00291         Common::String needleStr(needle);
00292         haystack.toLowercase();
00293         needleStr.toLowercase();
00294         return haystack.contains(needleStr);
00295     }
00296 }
00297 
00298 void TextSplitter::expectString(const char *expected) {
00299     if (!_currLine)
00300         error("Expected `%s', got EOF on file %s", expected, _fname.c_str());
00301     if (scumm_stricmp(getCurrentLine(), expected) != 0)
00302         error("Expected `%s', got '%s' on file %s", expected, getCurrentLine(), _fname.c_str());
00303     nextLine();
00304 }
00305 
00306 void TextSplitter::scanString(const char *fmt, int field_count, ...) {
00307     if (!_currLine)
00308         error("Expected line of format '%s', got EOF on file %s", fmt, _fname.c_str());
00309 
00310     va_list va;
00311     va_start(va, field_count);
00312 
00313     parse(getCurrentLine(), fmt, field_count, va);
00314 
00315     va_end(va);
00316 
00317     nextLine();
00318 }
00319 
00320 void TextSplitter::scanStringAtOffset(int offset, const char *fmt, int field_count, ...) {
00321     if (!_currLine)
00322         error("Expected line of format '%s', got EOF on file %s", fmt, _fname.c_str());
00323 
00324     va_list va;
00325     va_start(va, field_count);
00326 
00327     parse(getCurrentLine() + offset, fmt, field_count, va);
00328 
00329     va_end(va);
00330 
00331     nextLine();
00332 }
00333 
00334 void TextSplitter::scanStringNoNewLine(const char *fmt, int field_count, ...) {
00335     if (!_currLine)
00336         error("Expected line of format '%s', got EOF on file %s", fmt, _fname.c_str());
00337 
00338     va_list va;
00339     va_start(va, field_count);
00340 
00341     parse(getCurrentLine(), fmt, field_count, va);
00342 
00343     va_end(va);
00344 }
00345 
00346 void TextSplitter::scanStringAtOffsetNoNewLine(int offset, const char *fmt, int field_count, ...) {
00347     if (!_currLine)
00348         error("Expected line of format '%s', got EOF on file %s", fmt, _fname.c_str());
00349 
00350     va_list va;
00351     va_start(va, field_count);
00352 
00353     parse(getCurrentLine() + offset, fmt, field_count, va);
00354 
00355     va_end(va);
00356 }
00357 
00358 void TextSplitter::processLine() {
00359     if (isEof())
00360         return;
00361 
00362     _currLine = _lines[_lineIndex++];
00363 
00364     // Cut off comments
00365     char *comment_start = strchr(_currLine, '#');
00366     if (comment_start)
00367         *comment_start = '\0';
00368 
00369     // Cut off trailing whitespace (including '\r')
00370     char *strend = strchr(_currLine, '\0');
00371     while (strend > _currLine && Common::isSpace(strend[-1]))
00372         strend--;
00373     *strend = '\0';
00374 
00375     // Skip blank lines
00376     if (*_currLine == '\0')
00377         nextLine();
00378 
00379     // Convert to lower case
00380     if (!isEof())
00381         for (char *s = _currLine; *s != '\0'; s++)
00382             *s = tolower(*s);
00383 }
00384 
00385 } // end of namespace Grim


Generated on Sat Feb 23 2019 05:01:18 for ResidualVM by doxygen 1.7.1
curved edge   curved edge