static void DedicatedHandleKeyInput() { static char input_line[1024] = ""; if (!InputWaiting()) return; if (_exit_game) return; #if defined(UNIX) || defined(__OS2__) || defined(PSP) if (fgets(input_line, lengthof(input_line), stdin) == NULL) return; #else /* Handle console input, and signal console thread, it can accept input again */ assert_compile(lengthof(_win_console_thread_buffer) <= lengthof(input_line)); strecpy(input_line, _win_console_thread_buffer, lastof(input_line)); SetEvent(_hWaitForInputHandling); #endif /* Remove trailing \r or \n */ for (char *c = input_line; *c != '\0'; c++) { if (*c == '\n' || *c == '\r' || c == lastof(input_line)) { *c = '\0'; break; } } str_validate(input_line, lastof(input_line)); IConsoleCmdExec(input_line); // execute command }
/** Initialize the highscore table to 0 and if any file exists, load in values */ void LoadFromHighScore() { FILE *fp = fopen(_highscore_file, "rb"); memset(_highscore_table, 0, sizeof(_highscore_table)); if (fp != NULL) { uint i; HighScore *hs; for (i = 0; i < SP_SAVED_HIGHSCORE_END; i++) { for (hs = _highscore_table[i]; hs != endof(_highscore_table[i]); hs++) { byte length; if (fread(&length, sizeof(length), 1, fp) != 1 || fread(hs->company, min<int>(lengthof(hs->company), length), 1, fp) > 1 || // Yes... could be 0 bytes too fread(&hs->score, sizeof(hs->score), 1, fp) != 1 || fseek(fp, 2, SEEK_CUR) == -1) { // XXX - placeholder for hs->title, not saved anymore; compatibility DEBUG(misc, 1, "Highscore corrupted"); i = SP_SAVED_HIGHSCORE_END; break; } str_validate(hs->company, lastof(hs->company), SVS_NONE); hs->title = EndGameGetPerformanceTitleFromValue(hs->score); } } fclose(fp); } }
static void DedicatedHandleKeyInput() { static char input_line[1024] = ""; if (!InputWaiting()) return; if (_exit_game) return; #if defined(UNIX) || defined(__OS2__) || defined(PSP) if (fgets(input_line, lengthof(input_line), stdin) == NULL) return; #else /* Handle console input, and singal console thread, it can accept input again */ assert_compile(lengthof(_win_console_thread_buffer) <= lengthof(input_line)); strecpy(input_line, _win_console_thread_buffer, lastof(input_line)); SetEvent(_hWaitForInputHandling); #endif /* strtok() does not 'forget' \r\n if the string starts with it, * so we have to manually remove that! */ strtok(input_line, "\r\n"); for (char *c = input_line; *c != '\0'; c++) { if (*c == '\n' || *c == '\r' || c == lastof(input_line)) { *c = '\0'; break; } } str_validate(input_line, lastof(input_line)); IConsoleCmdExec(input_line); // execute command }
/** * Handle the printing of text entered into the console or redirected there * by any other means. Text can be redirected to other clients in a network game * as well as to a logfile. If the network server is a dedicated server, all activities * are also logged. All lines to print are added to a temporary buffer which can be * used as a history to print them onscreen * @param colour_code the colour of the command. Red in case of errors, etc. * @param string the message entered or output on the console (notice, error, etc.) */ void IConsolePrint(ConsoleColour colour_code, const char *string) { char *str; #ifdef ENABLE_NETWORK if (_redirect_console_to_client != INVALID_CLIENT_ID) { /* Redirect the string to the client */ NetworkServerSendRcon(_redirect_console_to_client, colour_code, string); return; } #endif /* Create a copy of the string, strip if of colours and invalid * characters and (when applicable) assign it to the console buffer */ str = strdup(string); str_strip_colours(str); str_validate(str, str + strlen(str)); if (_network_dedicated) { fprintf(stdout, "%s%s\n", GetLogPrefix(), str); fflush(stdout); IConsoleWriteToLogFile(str); free(str); // free duplicated string since it's not used anymore return; } IConsoleWriteToLogFile(str); IConsoleGUIPrint(colour_code, str); }
/** * Construct a new in-memory group of an Ini file. * @param parent the file we belong to * @param name the name of the group * @param last the last element of the name of the group */ IniGroup::IniGroup(IniLoadFile *parent, const char *name, const char *last) : next(nullptr), type(IGT_VARIABLES), item(nullptr), comment(nullptr) { this->name = stredup(name, last); str_validate(this->name, this->name + strlen(this->name)); this->last_item = &this->item; *parent->last_group = this; parent->last_group = &this->next; if (parent->list_group_names != nullptr) { for (uint i = 0; parent->list_group_names[i] != nullptr; i++) { if (strcmp(this->name, parent->list_group_names[i]) == 0) { this->type = IGT_LIST; return; } } } if (parent->seq_group_names != nullptr) { for (uint i = 0; parent->seq_group_names[i] != nullptr; i++) { if (strcmp(this->name, parent->seq_group_names[i]) == 0) { this->type = IGT_SEQUENCE; return; } } } }
/** * Construct a new in-memory item of an Ini file. * @param parent the group we belong to * @param name the name of the item * @param last the last element of the name of the item */ IniItem::IniItem(IniGroup *parent, const char *name, const char *last) : next(nullptr), value(nullptr), comment(nullptr) { this->name = stredup(name, last); str_validate(this->name, this->name + strlen(this->name)); *parent->last_item = this; parent->last_item = &this->next; }
template <> inline const char *GetParam(ForceType<const char *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { sq_tostring(vm, index); const SQChar *tmp; sq_getstring(vm, -1, &tmp); char *tmp_str = strdup(SQ2OTTD(tmp)); sq_poptop(vm); *ptr->Append() = (void *)tmp_str; str_validate(tmp_str, tmp_str + strlen(tmp_str)); return tmp_str; }
static inline bool CheckOldSavegameType(FILE *f, char *temp, const char *last, uint len) { assert(last - temp + 1 >= (int)len); if (fread(temp, 1, len, f) != len) { temp[0] = '\0'; // if reading failed, make the name empty return false; } bool ret = VerifyOldNameChecksum(temp, len); temp[len - 2] = '\0'; // name is nul-terminated in savegame, but it's better to be sure str_validate(temp, last); return ret; }
/** * Reads a string till it finds a '\0' in the stream. * @param buffer The buffer to put the data into. * @param size The size of the buffer. * @param allow_newlines Whether the string validation should remove newlines. */ void Packet::Recv_string(char *buffer, size_t size, bool allow_newlines) { PacketSize pos; char *bufp = buffer; const char *last = buffer + size - 1; /* Don't allow reading from a closed socket */ if (cs->HasClientQuit()) return; pos = this->pos; while (--size > 0 && pos < this->size && (*buffer++ = this->buffer[pos++]) != '\0') {} if (size == 0 || pos == this->size) { *buffer = '\0'; /* If size was sooner to zero then the string in the stream * skip till the \0, so than packet can be read out correctly for the rest */ while (pos < this->size && this->buffer[pos] != '\0') pos++; pos++; } this->pos = pos; str_validate(bufp, last, allow_newlines); }
/** * Scans the string for valid characters and if it finds invalid ones, * replaces them with a question mark '?'. * @param str the string to validate */ void ValidateString(const char *str) { /* We know it is '\0' terminated. */ str_validate(const_cast<char *>(str), str + strlen(str) + 1); }
bool SetFallbackFont(FreeTypeSettings *settings, const char *language_isocode, int winlangid, MissingGlyphSearcher *callback) { const char *str; bool result = false; callback->FindMissingGlyphs(&str); #if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5) if (MacOSVersionIsAtLeast(10, 5, 0)) { /* Determine fallback font using CoreText. This uses the language isocode * to find a suitable font. CoreText is available from 10.5 onwards. */ char lang[16]; if (strcmp(language_isocode, "zh_TW") == 0) { /* Traditional Chinese */ strecpy(lang, "zh-Hant", lastof(lang)); } else if (strcmp(language_isocode, "zh_CN") == 0) { /* Simplified Chinese */ strecpy(lang, "zh-Hans", lastof(lang)); } else if (strncmp(language_isocode, "ur", 2) == 0) { /* The urdu alphabet is variant of persian. As OS X has no default * font that advertises an urdu language code, search for persian * support instead. */ strecpy(lang, "fa", lastof(lang)); } else { /* Just copy the first part of the isocode. */ strecpy(lang, language_isocode, lastof(lang)); char *sep = strchr(lang, '_'); if (sep != NULL) *sep = '\0'; } CFStringRef lang_code; lang_code = CFStringCreateWithCString(kCFAllocatorDefault, lang, kCFStringEncodingUTF8); /* Create a font iterator and iterate over all fonts that * are available to the application. */ ATSFontIterator itr; ATSFontRef font; ATSFontIteratorCreate(kATSFontContextLocal, NULL, NULL, kATSOptionFlagsUnRestrictedScope, &itr); while (!result && ATSFontIteratorNext(itr, &font) == noErr) { /* Get CoreText font handle. */ CTFontRef font_ref = CTFontCreateWithPlatformFont(font, 0.0, NULL, NULL); CFArrayRef langs = CTFontCopySupportedLanguages(font_ref); if (langs != NULL) { /* Font has a list of supported languages. */ for (CFIndex i = 0; i < CFArrayGetCount(langs); i++) { CFStringRef lang = (CFStringRef)CFArrayGetValueAtIndex(langs, i); if (CFStringCompare(lang, lang_code, kCFCompareAnchored) == kCFCompareEqualTo) { /* Lang code is supported by font, get full font name. */ CFStringRef font_name = CTFontCopyFullName(font_ref); char name[128]; CFStringGetCString(font_name, name, lengthof(name), kCFStringEncodingUTF8); CFRelease(font_name); /* Skip some inappropriate or ugly looking fonts that have better alternatives. */ if (strncmp(name, "Courier", 7) == 0 || strncmp(name, "Apple Symbols", 13) == 0 || strncmp(name, ".Aqua", 5) == 0 || strncmp(name, "LastResort", 10) == 0 || strncmp(name, "GB18030 Bitmap", 14) == 0) continue; /* Save result. */ callback->SetFontNames(settings, name); DEBUG(freetype, 2, "CT-Font for %s: %s", language_isocode, name); result = true; break; } } CFRelease(langs); } CFRelease(font_ref); } ATSFontIteratorRelease(&itr); CFRelease(lang_code); } else #endif { #if (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5) && !__LP64__ /* Determine fallback font using ATSUI. This uses a string sample with * missing characters. This is not failure-proof, but a better way like * using the isocode as in the CoreText code path is not available. * ATSUI was deprecated with 10.6 and is only partially available in * 64-bit mode. */ /* Remove all control characters in the range from SCC_CONTROL_START to * SCC_CONTROL_END as well as all ASCII < 0x20 from the string as it will * mess with the automatic font detection */ char buff[256]; // This length is enough to find a suitable replacement font strecpy(buff, str, lastof(buff)); str_validate(buff, lastof(buff), SVS_ALLOW_NEWLINE); /* Extract a UniChar representation of the sample string. */ CFStringRef cf_str = CFStringCreateWithCString(kCFAllocatorDefault, buff, kCFStringEncodingUTF8); if (cf_str == NULL) { /* Something went wrong. Corrupt/invalid sample string? */ return false; } CFIndex str_len = CFStringGetLength(cf_str); UniChar string[str_len]; CFStringGetCharacters(cf_str, CFRangeMake(0, str_len), string); /* Create a default text style with the default font. */ ATSUStyle style; ATSUCreateStyle(&style); /* Create a text layout object from the sample string using the text style. */ UniCharCount run_len = kATSUToTextEnd; ATSUTextLayout text_layout; ATSUCreateTextLayoutWithTextPtr(string, kATSUFromTextBeginning, kATSUToTextEnd, str_len, 1, &run_len, &style, &text_layout); /* Try to match a font for the sample text. ATSUMatchFontsToText stops after * it finds the first continuous character run not renderable with the currently * selected font starting at offset. The matching needs to be repeated until * the end of the string is reached to make sure the fallback font matches for * all characters in the string and not only the first run. */ UniCharArrayOffset offset = kATSUFromTextBeginning; OSStatus os_err; do { ATSUFontID font; UniCharCount run_len; os_err = ATSUMatchFontsToText(text_layout, offset, kATSUToTextEnd, &font, &offset, &run_len); if (os_err == kATSUFontsMatched) { /* Found a better fallback font. Update the text layout * object with the new font. */ ATSUAttributeTag tag = kATSUFontTag; ByteCount size = sizeof(font); ATSUAttributeValuePtr val = &font; ATSUSetAttributes(style, 1, &tag, &size, &val); offset += run_len; } /* Exit if the end of the string is reached or some other error occurred. */ } while (os_err == kATSUFontsMatched && offset < (UniCharArrayOffset)str_len); if (os_err == noErr || os_err == kATSUFontsMatched) { /* ATSUMatchFontsToText exited normally. Extract font * out of the text layout object. */ ATSUFontID font; ByteCount act_len; ATSUGetAttribute(style, kATSUFontTag, sizeof(font), &font, &act_len); /* Get unique font name. The result is not a c-string, we have * to leave space for a \0 and terminate it ourselves. */ char name[128]; ATSUFindFontName(font, kFontUniqueName, kFontNoPlatformCode, kFontNoScriptCode, kFontNoLanguageCode, 127, name, &act_len, NULL); name[act_len > 127 ? 127 : act_len] = '\0'; /* Save Result. */ callback->SetFontNames(settings, name); DEBUG(freetype, 2, "ATSUI-Font for %s: %s", language_isocode, name); result = true; } ATSUDisposeTextLayout(text_layout); ATSUDisposeStyle(style); CFRelease(cf_str); #endif } if (result && strncmp(settings->medium.font, "Geeza Pro", 9) == 0) { /* The font 'Geeza Pro' is often found for arabic characters, but * it has the 'tiny' problem of not having any latin characters. * 'Arial Unicode MS' on the other hand has arabic and latin glyphs, * but seems to 'forget' to inform the OS about this fact. Manually * substitute the latter for the former if it is loadable. */ bool ft_init = _library != NULL; FT_Face face; /* Init FreeType if needed. */ if ((ft_init || FT_Init_FreeType(&_library) == FT_Err_Ok) && GetFontByFaceName("Arial Unicode MS", &face) == FT_Err_Ok) { FT_Done_Face(face); callback->SetFontNames(settings, "Arial Unicode MS"); DEBUG(freetype, 1, "Replacing font 'Geeza Pro' with 'Arial Unicode MS'"); } if (!ft_init) { /* Uninit FreeType if we did the init. */ FT_Done_FreeType(_library); _library = NULL; } } callback->FindMissingGlyphs(NULL); return result; }
/** * Try to add a fios item set with the given filename. * @param filename the full path to the file to read * @param basepath_length amount of characters to chop of before to get a relative filename * @return true if the file is added. */ bool FiosFileScanner::AddFile(const char *filename, size_t basepath_length, const char *tar_filename) { const char *ext = strrchr(filename, '.'); if (ext == NULL) return false; char fios_title[64]; fios_title[0] = '\0'; // reset the title; FiosType type = this->callback_proc(this->mode, filename, ext, fios_title, lastof(fios_title)); if (type == FIOS_TYPE_INVALID) return false; for (const FiosItem *fios = _fios_items.Begin(); fios != _fios_items.End(); fios++) { if (strcmp(fios->name, filename) == 0) return false; } FiosItem *fios = _fios_items.Append(); #ifdef WIN32 struct _stat sb; if (_tstat(OTTD2FS(filename), &sb) == 0) { #else struct stat sb; if (stat(filename, &sb) == 0) { #endif fios->mtime = sb.st_mtime; } else { fios->mtime = 0; } fios->type = type; strecpy(fios->name, filename, lastof(fios->name)); /* If the file doesn't have a title, use its filename */ const char *t = fios_title; if (StrEmpty(fios_title)) { t = strrchr(filename, PATHSEPCHAR); t = (t == NULL) ? filename : (t + 1); } strecpy(fios->title, t, lastof(fios->title)); str_validate(fios->title, lastof(fios->title)); return true; } /** * Fill the list of the files in a directory, according to some arbitrary rule. * @param mode The mode we are in. Some modes don't allow 'parent'. * @param callback_proc The function that is called where you need to do the filtering. * @param subdir The directory from where to start (global) searching. */ static void FiosGetFileList(SaveLoadDialogMode mode, fios_getlist_callback_proc *callback_proc, Subdirectory subdir) { struct stat sb; struct dirent *dirent; DIR *dir; FiosItem *fios; int sort_start; char d_name[sizeof(fios->name)]; _fios_items.Clear(); /* A parent directory link exists if we are not in the root directory */ if (!FiosIsRoot(_fios_path)) { fios = _fios_items.Append(); fios->type = FIOS_TYPE_PARENT; fios->mtime = 0; strecpy(fios->name, "..", lastof(fios->name)); strecpy(fios->title, ".. (Parent directory)", lastof(fios->title)); } /* Show subdirectories */ if ((dir = ttd_opendir(_fios_path)) != NULL) { while ((dirent = readdir(dir)) != NULL) { strecpy(d_name, FS2OTTD(dirent->d_name), lastof(d_name)); /* found file must be directory, but not '.' or '..' */ if (FiosIsValidFile(_fios_path, dirent, &sb) && S_ISDIR(sb.st_mode) && (!FiosIsHiddenFile(dirent) || strncasecmp(d_name, PERSONAL_DIR, strlen(d_name)) == 0) && strcmp(d_name, ".") != 0 && strcmp(d_name, "..") != 0) { fios = _fios_items.Append(); fios->type = FIOS_TYPE_DIR; fios->mtime = 0; strecpy(fios->name, d_name, lastof(fios->name)); snprintf(fios->title, lengthof(fios->title), "%s" PATHSEP " (Directory)", d_name); str_validate(fios->title, lastof(fios->title)); } } closedir(dir); } /* Sort the subdirs always by name, ascending, remember user-sorting order */ { SortingBits order = _savegame_sort_order; _savegame_sort_order = SORT_BY_NAME | SORT_ASCENDING; QSortT(_fios_items.Begin(), _fios_items.Length(), CompareFiosItems); _savegame_sort_order = order; } /* This is where to start sorting for the filenames */ sort_start = _fios_items.Length(); /* Show files */ FiosFileScanner scanner(mode, callback_proc); if (subdir == NO_DIRECTORY) { scanner.Scan(NULL, _fios_path, false); } else { scanner.Scan(NULL, subdir, true, true); } QSortT(_fios_items.Get(sort_start), _fios_items.Length() - sort_start, CompareFiosItems); /* Show drives */ FiosGetDrives(); _fios_items.Compact(); } /** * Get the title of a file, which (if exists) is stored in a file named * the same as the data file but with '.title' added to it. * @param file filename to get the title for * @param title the title buffer to fill * @param last the last element in the title buffer * @param subdir the sub directory to search in */ static void GetFileTitle(const char *file, char *title, const char *last, Subdirectory subdir) { char buf[MAX_PATH]; strecpy(buf, file, lastof(buf)); strecat(buf, ".title", lastof(buf)); FILE *f = FioFOpenFile(buf, "r", subdir); if (f == NULL) return; size_t read = fread(title, 1, last - title, f); assert(title + read <= last); title[read] = '\0'; str_validate(title, last); FioFCloseFile(f); }
/** * Load the Ini file's data from the disk. * @param filename the file to load. * @param subdir the sub directory to load the file from. * @pre nothing has been loaded yet. */ void IniLoadFile::LoadFromDisk(const char *filename, Subdirectory subdir) { assert(this->last_group == &this->group); char buffer[1024]; IniGroup *group = nullptr; char *comment = nullptr; uint comment_size = 0; uint comment_alloc = 0; size_t end; FILE *in = this->OpenFile(filename, subdir, &end); if (in == nullptr) return; end += ftell(in); /* for each line in the file */ while ((size_t)ftell(in) < end && fgets(buffer, sizeof(buffer), in)) { char c, *s; /* trim whitespace from the left side */ for (s = buffer; *s == ' ' || *s == '\t'; s++) {} /* trim whitespace from right side. */ char *e = s + strlen(s); while (e > s && ((c = e[-1]) == '\n' || c == '\r' || c == ' ' || c == '\t')) e--; *e = '\0'; /* Skip comments and empty lines outside IGT_SEQUENCE groups. */ if ((group == nullptr || group->type != IGT_SEQUENCE) && (*s == '#' || *s == ';' || *s == '\0')) { uint ns = comment_size + (e - s + 1); uint a = comment_alloc; /* add to comment */ if (ns > a) { a = max(a, 128U); do a *= 2; while (a < ns); comment = ReallocT(comment, comment_alloc = a); } uint pos = comment_size; comment_size += (e - s + 1); comment[pos + e - s] = '\n'; // comment newline memcpy(comment + pos, s, e - s); // copy comment contents continue; } /* it's a group? */ if (s[0] == '[') { if (e[-1] != ']') { this->ReportFileError("ini: invalid group name '", buffer, "'"); } else { e--; } s++; // skip [ group = new IniGroup(this, s, e - 1); if (comment_size != 0) { group->comment = stredup(comment, comment + comment_size - 1); comment_size = 0; } } else if (group != nullptr) { if (group->type == IGT_SEQUENCE) { /* A sequence group, use the line as item name without further interpretation. */ IniItem *item = new IniItem(group, buffer, e - 1); if (comment_size) { item->comment = stredup(comment, comment + comment_size - 1); comment_size = 0; } continue; } char *t; /* find end of keyname */ if (*s == '\"') { s++; for (t = s; *t != '\0' && *t != '\"'; t++) {} if (*t == '\"') *t = ' '; } else { for (t = s; *t != '\0' && *t != '=' && *t != '\t' && *t != ' '; t++) {} } /* it's an item in an existing group */ IniItem *item = new IniItem(group, s, t - 1); if (comment_size != 0) { item->comment = stredup(comment, comment + comment_size - 1); comment_size = 0; } /* find start of parameter */ while (*t == '=' || *t == ' ' || *t == '\t') t++; bool quoted = (*t == '\"'); /* remove starting quotation marks */ if (*t == '\"') t++; /* remove ending quotation marks */ e = t + strlen(t); if (e > t && e[-1] == '\"') e--; *e = '\0'; /* If the value was not quoted and empty, it must be nullptr */ item->value = (!quoted && e == t) ? nullptr : stredup(t); if (item->value != nullptr) str_validate(item->value, item->value + strlen(item->value)); } else { /* it's an orphan item */ this->ReportFileError("ini: '", buffer, "' outside of group"); } } if (comment_size > 0) { this->comment = stredup(comment, comment + comment_size - 1); comment_size = 0; } free(comment); fclose(in); }