const char *SaveKeycodes(const Hotkey<T> *hotkey) { static char buf[128]; buf[0] = '\0'; for (uint i = 0; i < hotkey->keycodes.Length(); i++) { const char *str = KeycodeToString(hotkey->keycodes[i]); if (i > 0) strecat(buf, ",", lastof(buf)); strecat(buf, str, lastof(buf)); } return buf; }
void DetermineBasePaths(const char *exe) { char tmp[MAX_PATH]; TCHAR path[MAX_PATH]; #ifdef WITH_PERSONAL_DIR if (SUCCEEDED(OTTDSHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, path))) { strecpy(tmp, FS2OTTD(path), lastof(tmp)); AppendPathSeparator(tmp, lastof(tmp)); strecat(tmp, PERSONAL_DIR, lastof(tmp)); AppendPathSeparator(tmp, lastof(tmp)); _searchpaths[SP_PERSONAL_DIR] = stredup(tmp); } else { _searchpaths[SP_PERSONAL_DIR] = NULL; } if (SUCCEEDED(OTTDSHGetFolderPath(NULL, CSIDL_COMMON_DOCUMENTS, NULL, SHGFP_TYPE_CURRENT, path))) { strecpy(tmp, FS2OTTD(path), lastof(tmp)); AppendPathSeparator(tmp, lastof(tmp)); strecat(tmp, PERSONAL_DIR, lastof(tmp)); AppendPathSeparator(tmp, lastof(tmp)); _searchpaths[SP_SHARED_DIR] = stredup(tmp); } else { _searchpaths[SP_SHARED_DIR] = NULL; } #else _searchpaths[SP_PERSONAL_DIR] = NULL; _searchpaths[SP_SHARED_DIR] = NULL; #endif /* Get the path to working directory of OpenTTD */ getcwd(tmp, lengthof(tmp)); AppendPathSeparator(tmp, lastof(tmp)); _searchpaths[SP_WORKING_DIR] = stredup(tmp); if (!GetModuleFileName(NULL, path, lengthof(path))) { DEBUG(misc, 0, "GetModuleFileName failed (%lu)\n", GetLastError()); _searchpaths[SP_BINARY_DIR] = NULL; } else { TCHAR exec_dir[MAX_PATH]; _tcsncpy(path, convert_to_fs(exe, path, lengthof(path)), lengthof(path)); if (!GetFullPathName(path, lengthof(exec_dir), exec_dir, NULL)) { DEBUG(misc, 0, "GetFullPathName failed (%lu)\n", GetLastError()); _searchpaths[SP_BINARY_DIR] = NULL; } else { strecpy(tmp, convert_from_fs(exec_dir, tmp, lengthof(tmp)), lastof(tmp)); char *s = strrchr(tmp, PATHSEPCHAR); *(s + 1) = '\0'; _searchpaths[SP_BINARY_DIR] = stredup(tmp); } } _searchpaths[SP_INSTALLATION_DIR] = NULL; _searchpaths[SP_APPLICATION_BUNDLE_DIR] = NULL; }
/** * Create an autosave. The default name is "autosave#.sav". However with * the setting 'keep_all_autosave' the name defaults to company-name + date */ static void DoAutosave() { char buf[MAX_PATH]; #if defined(PSP) /* Autosaving in networking is too time expensive for the PSP */ if (_networking) return; #endif /* PSP */ if (_settings_client.gui.keep_all_autosave) { GenerateDefaultSaveName(buf, lastof(buf)); strecat(buf, ".sav", lastof(buf)); } else { static int _autosave_ctr = 0; /* generate a savegame name and number according to _settings_client.gui.max_num_autosaves */ snprintf(buf, sizeof(buf), "autosave%d.sav", _autosave_ctr); if (++_autosave_ctr >= _settings_client.gui.max_num_autosaves) _autosave_ctr = 0; } DEBUG(sl, 2, "Autosaving to '%s'", buf); if (SaveOrLoad(buf, SL_SAVE, AUTOSAVE_DIR) != SL_OK) { ShowErrorMessage(STR_ERROR_AUTOSAVE_FAILED, INVALID_STRING_ID, WL_ERROR); } }
/** Print out the current debug-level * Just return a string with the values of all the debug categorites * @return string with debug-levels */ const char *GetDebugString() { const DebugLevel *i; static char dbgstr[150]; char dbgval[20]; memset(dbgstr, 0, sizeof(dbgstr)); i = debug_level; snprintf(dbgstr, sizeof(dbgstr), "%s=%d", i->name, *i->level); for (i++; i != endof(debug_level); i++) { snprintf(dbgval, sizeof(dbgval), ", %s=%d", i->name, *i->level); strecat(dbgstr, dbgval, lastof(dbgstr)); } return dbgstr; }
void Squirrel::PrintFunc(HSQUIRRELVM vm, const SQChar *s, ...) { va_list arglist; SQChar buf[1024]; va_start(arglist, s); vseprintf(buf, lastof(buf) - 2, s, arglist); va_end(arglist); strecat(buf, "\n", lastof(buf)); /* Check if we have a custom print function */ SQPrintFunc *func = ((Squirrel *)sq_getforeignptr(vm))->print_func; if (func == NULL) { printf("%s", buf); } else { (*func)(false, buf); } }
/** * Clone the custom name of a vehicle, adding or incrementing a number. * @param src Source vehicle, with a custom name. * @param dst Destination vehicle. */ static void CloneVehicleName(const Vehicle *src, Vehicle *dst) { char buf[256]; /* Find the position of the first digit in the last group of digits. */ size_t number_position; for (number_position = strlen(src->name); number_position > 0; number_position--) { /* The design of UTF-8 lets this work simply without having to check * for UTF-8 sequences. */ if (src->name[number_position - 1] < '0' || src->name[number_position - 1] > '9') break; } /* Format buffer and determine starting number. */ int num; byte padding = 0; if (number_position == strlen(src->name)) { /* No digit at the end, so start at number 2. */ strecpy(buf, src->name, lastof(buf)); strecat(buf, " ", lastof(buf)); number_position = strlen(buf); num = 2; } else { /* Found digits, parse them and start at the next number. */ strecpy(buf, src->name, lastof(buf)); buf[number_position] = '\0'; char *endptr; num = strtol(&src->name[number_position], &endptr, 10) + 1; padding = endptr - &src->name[number_position]; } /* Check if this name is already taken. */ for (int max_iterations = 1000; max_iterations > 0; max_iterations--, num++) { /* Attach the number to the temporary name. */ seprintf(&buf[number_position], lastof(buf), "%0*d", padding, num); /* Check the name is unique. */ if (IsUniqueVehicleName(buf)) { dst->name = strdup(buf); break; } } /* All done. If we didn't find a name, it'll just use its default. */ }
/** * Determine the full filename of a piece of content information * @param ci the information to get the filename from * @param compressed should the filename end with .gz? * @return a statically allocated buffer with the filename or * NULL when no filename could be made. */ static char *GetFullFilename(const ContentInfo *ci, bool compressed) { Subdirectory dir; switch (ci->type) { default: return NULL; case CONTENT_TYPE_BASE_GRAPHICS: dir = DATA_DIR; break; case CONTENT_TYPE_BASE_MUSIC: dir = GM_DIR; break; case CONTENT_TYPE_BASE_SOUNDS: dir = DATA_DIR; break; case CONTENT_TYPE_NEWGRF: dir = DATA_DIR; break; case CONTENT_TYPE_AI: dir = AI_DIR; break; case CONTENT_TYPE_AI_LIBRARY: dir = AI_LIBRARY_DIR; break; case CONTENT_TYPE_SCENARIO: dir = SCENARIO_DIR; break; case CONTENT_TYPE_HEIGHTMAP: dir = HEIGHTMAP_DIR; break; } static char buf[MAX_PATH]; FioGetFullPath(buf, lengthof(buf), SP_AUTODOWNLOAD_DIR, dir, ci->filename); strecat(buf, compressed ? ".tar.gz" : ".tar", lastof(buf)); return buf; }
/** * Create an autosave. The default name is "autosave#.sav". However with * the setting 'keep_all_autosave' the name defaults to company-name + date */ static void DoAutosave() { char buf[MAX_PATH]; if (_settings_client.gui.keep_all_autosave) { GenerateDefaultSaveName(buf, lastof(buf)); strecat(buf, ".sav", lastof(buf)); } else { static int _autosave_ctr = 0; /* generate a savegame name and number according to _settings_client.gui.max_num_autosaves */ seprintf(buf, lastof(buf), "autosave%d.sav", _autosave_ctr); if (++_autosave_ctr >= _settings_client.gui.max_num_autosaves) _autosave_ctr = 0; } DEBUG(sl, 2, "Autosaving to '%s'", buf); if (SaveOrLoad(buf, SLO_SAVE, DFT_GAME_FILE, AUTOSAVE_DIR) != SL_OK) { ShowErrorMessage(STR_ERROR_AUTOSAVE_FAILED, INVALID_STRING_ID, WL_ERROR); } }
static char *RandomPart(char *buf, GRFTownName *t, uint32 seed, byte id, const char *last) { assert(t != NULL); for (int i = 0; i < t->nbparts[id]; i++) { byte count = t->partlist[id][i].bitcount; uint16 maxprob = t->partlist[id][i].maxprob; uint32 r = (GB(seed, t->partlist[id][i].bitstart, count) * maxprob) >> count; for (int j = 0; j < t->partlist[id][i].partcount; j++) { byte prob = t->partlist[id][i].parts[j].prob; maxprob -= GB(prob, 0, 7); if (maxprob > r) continue; if (HasBit(prob, 7)) { buf = RandomPart(buf, t, seed, t->partlist[id][i].parts[j].data.id, last); } else { buf = strecat(buf, t->partlist[id][i].parts[j].data.text, last); } break; } } return buf; }
/** * Convert a hotkey to it's string representation so it can be written to the * config file. Seperate parts of the keycode (like "CTRL" and "F1" are split * by a '+'. * @param keycode The keycode to convert to a string. * @return A string representation of this keycode. * @note The return value is a static buffer, strdup the result before calling * this function again. */ static const char *KeycodeToString(uint16 keycode) { static char buf[32]; buf[0] = '\0'; bool first = true; if (keycode & WKC_GLOBAL_HOTKEY) { strecat(buf, "GLOBAL", lastof(buf)); first = false; } if (keycode & WKC_SHIFT) { if (!first) strecat(buf, "+", lastof(buf)); strecat(buf, "SHIFT", lastof(buf)); first = false; } if (keycode & WKC_CTRL) { if (!first) strecat(buf, "+", lastof(buf)); strecat(buf, "CTRL", lastof(buf)); first = false; } if (keycode & WKC_ALT) { if (!first) strecat(buf, "+", lastof(buf)); strecat(buf, "ALT", lastof(buf)); first = false; } if (keycode & WKC_META) { if (!first) strecat(buf, "+", lastof(buf)); strecat(buf, "META", lastof(buf)); first = false; } if (!first) strecat(buf, "+", lastof(buf)); keycode = keycode & ~WKC_SPECIAL_KEYS; for (uint i = 0; i < lengthof(_keycode_to_name); i++) { if (_keycode_to_name[i].keycode == keycode) { strecat(buf, _keycode_to_name[i].name, lastof(buf)); return buf; } } assert(keycode < 128); char key[2]; key[0] = keycode; key[1] = '\0'; strecat(buf, key, lastof(buf)); return buf; }
/** * Save the Ini file's data to the disk. * @param filename the file to save to. * @return true if saving succeeded. */ bool IniFile::SaveToDisk(const char *filename) { /* * First write the configuration to a (temporary) file and then rename * that file. This to prevent that when OpenTTD crashes during the save * you end up with a truncated configuration file. */ char file_new[MAX_PATH]; strecpy(file_new, filename, lastof(file_new)); strecat(file_new, ".new", lastof(file_new)); FILE *f = fopen(file_new, "w"); if (f == NULL) return false; for (const IniGroup *group = this->group; group != NULL; group = group->next) { if (group->comment) fputs(group->comment, f); fprintf(f, "[%s]\n", group->name); for (const IniItem *item = group->item; item != NULL; item = item->next) { if (item->comment != NULL) fputs(item->comment, f); /* protect item->name with quotes if needed */ if (strchr(item->name, ' ') != NULL || item->name[0] == '[') { fprintf(f, "\"%s\"", item->name); } else { fprintf(f, "%s", item->name); } fprintf(f, " = %s\n", item->value == NULL ? "" : item->value); } } if (this->comment) fputs(this->comment, f); /* * POSIX (and friends) do not guarantee that when a file is closed it is * flushed to the disk. So we manually flush it do disk if we have the * APIs to do so. We only need to flush the data as the metadata itself * (modification date etc.) is not important to us; only the real data is. */ #ifdef WITH_FDATASYNC int ret = fdatasync(fileno(f)); fclose(f); if (ret != 0) return false; #else fclose(f); #endif #if defined(WIN32) || defined(WIN64) /* _tcsncpy = strcpy is TCHAR is char, but isn't when TCHAR is wchar. */ #undef strncpy /* Allocate space for one more \0 character. */ TCHAR tfilename[MAX_PATH + 1], tfile_new[MAX_PATH + 1]; _tcsncpy(tfilename, OTTD2FS(filename), MAX_PATH); _tcsncpy(tfile_new, OTTD2FS(file_new), MAX_PATH); /* SHFileOperation wants a double '\0' terminated string. */ tfilename[MAX_PATH - 1] = '\0'; tfile_new[MAX_PATH - 1] = '\0'; tfilename[_tcslen(tfilename) + 1] = '\0'; tfile_new[_tcslen(tfile_new) + 1] = '\0'; /* Rename file without any user confirmation. */ SHFILEOPSTRUCT shfopt; MemSetT(&shfopt, 0); shfopt.wFunc = FO_MOVE; shfopt.fFlags = FOF_NOCONFIRMATION | FOF_NOCONFIRMMKDIR | FOF_NOERRORUI | FOF_SILENT; shfopt.pFrom = tfile_new; shfopt.pTo = tfilename; SHFileOperation(&shfopt); #else if (rename(file_new, filename) < 0) { DEBUG(misc, 0, "Renaming %s to %s failed; configuration not saved", file_new, filename); } #endif return true; }
/** * Draw the details for the given vehicle at the given position * * @param v current vehicle * @param left The left most coordinate to draw * @param right The right most coordinate to draw * @param y The y coordinate */ void DrawRoadVehDetails(const Vehicle *v, int left, int right, int y) { const RoadVehicle *rv = RoadVehicle::From(v); uint y_offset = rv->HasArticulatedPart() ? 15 : 0; // Draw the first line below the sprite of an articulated RV instead of after it. StringID str; Money feeder_share = 0; SetDParam(0, v->engine_type); SetDParam(1, v->build_year); SetDParam(2, v->value); DrawString(left, right, y + y_offset, STR_VEHICLE_INFO_BUILT_VALUE, TC_FROMSTRING, SA_LEFT | SA_STRIP); if (rv->HasArticulatedPart()) { CargoArray max_cargo; StringID subtype_text[NUM_CARGO]; char capacity[512]; memset(subtype_text, 0, sizeof(subtype_text)); for (const Vehicle *u = v; u != NULL; u = u->Next()) { max_cargo[u->cargo_type] += u->cargo_cap; if (u->cargo_cap > 0) { StringID text = GetCargoSubtypeText(u); if (text != STR_EMPTY) subtype_text[u->cargo_type] = text; } } GetString(capacity, STR_VEHICLE_DETAILS_TRAIN_ARTICULATED_RV_CAPACITY, lastof(capacity)); bool first = true; for (CargoID i = 0; i < NUM_CARGO; i++) { if (max_cargo[i] > 0) { char buffer[128]; SetDParam(0, i); SetDParam(1, max_cargo[i]); GetString(buffer, STR_JUST_CARGO, lastof(buffer)); if (!first) strecat(capacity, ", ", lastof(capacity)); strecat(capacity, buffer, lastof(capacity)); if (subtype_text[i] != 0) { GetString(buffer, subtype_text[i], lastof(buffer)); strecat(capacity, buffer, lastof(capacity)); } first = false; } } DrawString(left, right, y + FONT_HEIGHT_NORMAL + y_offset, capacity, TC_BLUE); for (const Vehicle *u = v; u != NULL; u = u->Next()) { if (u->cargo_cap == 0) continue; str = STR_VEHICLE_DETAILS_CARGO_EMPTY; if (!u->cargo.Empty()) { SetDParam(0, u->cargo_type); SetDParam(1, u->cargo.Count()); SetDParam(2, u->cargo.Source()); str = STR_VEHICLE_DETAILS_CARGO_FROM; feeder_share += u->cargo.FeederShare(); } DrawString(left, right, y + 2 * FONT_HEIGHT_NORMAL + 1 + y_offset, str); y_offset += FONT_HEIGHT_NORMAL + 1; } y_offset -= FONT_HEIGHT_NORMAL + 1; } else { SetDParam(0, v->cargo_type); SetDParam(1, v->cargo_cap); SetDParam(4, GetCargoSubtypeText(v)); DrawString(left, right, y + FONT_HEIGHT_NORMAL + y_offset, STR_VEHICLE_INFO_CAPACITY); str = STR_VEHICLE_DETAILS_CARGO_EMPTY; if (!v->cargo.Empty()) { SetDParam(0, v->cargo_type); SetDParam(1, v->cargo.Count()); SetDParam(2, v->cargo.Source()); str = STR_VEHICLE_DETAILS_CARGO_FROM; feeder_share += v->cargo.FeederShare(); } DrawString(left, right, y + 2 * FONT_HEIGHT_NORMAL + 1 + y_offset, str); } /* Draw Transfer credits text */ SetDParam(0, feeder_share); DrawString(left, right, y + 3 * FONT_HEIGHT_NORMAL + 3 + y_offset, STR_VEHICLE_INFO_FEEDER_CARGO_VALUE); }
DEF_UDP_RECEIVE_COMMAND(Client, PACKET_UDP_SERVER_RESPONSE) { NetworkGameList *item; /* Just a fail-safe.. should never happen */ if (_network_udp_server) return; DEBUG(net, 4, "[udp] server response from %s", client_addr->GetAddressAsString()); /* Find next item */ item = NetworkGameListAddItem(*client_addr); this->Recv_NetworkGameInfo(p, &item->info); item->info.compatible = true; { /* Checks whether there needs to be a request for names of GRFs and makes * the request if necessary. GRFs that need to be requested are the GRFs * that do not exist on the clients system and we do not have the name * resolved of, i.e. the name is still UNKNOWN_GRF_NAME_PLACEHOLDER. * The in_request array and in_request_count are used so there is no need * to do a second loop over the GRF list, which can be relatively expensive * due to the string comparisons. */ const GRFConfig *in_request[NETWORK_MAX_GRF_COUNT]; const GRFConfig *c; uint in_request_count = 0; for (c = item->info.grfconfig; c != NULL; c = c->next) { if (c->status == GCS_NOT_FOUND) item->info.compatible = false; if (c->status != GCS_NOT_FOUND || strcmp(c->name, UNKNOWN_GRF_NAME_PLACEHOLDER) != 0) continue; in_request[in_request_count] = c; in_request_count++; } if (in_request_count > 0) { /* There are 'unknown' GRFs, now send a request for them */ uint i; Packet packet(PACKET_UDP_CLIENT_GET_NEWGRFS); packet.Send_uint8(in_request_count); for (i = 0; i < in_request_count; i++) { this->Send_GRFIdentifier(&packet, in_request[i]); } this->SendPacket(&packet, &item->address); } } if (item->info.hostname[0] == '\0') { snprintf(item->info.hostname, sizeof(item->info.hostname), "%s", client_addr->GetHostname()); } if (client_addr->GetAddress()->ss_family == AF_INET6) { strecat(item->info.server_name, " (IPv6)", lastof(item->info.server_name)); } /* Check if we are allowed on this server based on the revision-match */ item->info.version_compatible = IsNetworkCompatibleVersion(item->info.server_revision); item->info.compatible &= item->info.version_compatible; // Already contains match for GRFs item->online = true; UpdateNetworkGameWindow(false); }
/** * 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); }