diff --git a/src/fios.cpp b/src/fios.cpp --- a/src/fios.cpp +++ b/src/fios.cpp @@ -495,3 +495,114 @@ void FiosGetHeightmapList(SaveLoadDialog FiosGetFileList(mode, &FiosGetHeightmapListCallback, strcmp(base_path, _fios_path) == 0 ? HEIGHTMAP_DIR : NO_DIRECTORY); } + +#if defined(ENABLE_NETWORK) +#include "core/smallvec_type.hpp" +#include "network/network_content.h" +#include "md5.h" + +/** Basic data to distinguish a scenario. Used in the server list window */ +struct ScenarioIdentifier { + uint32 scenid; ///< ID for the scenario (generated by content) + uint8 md5sum[16]; ///< MD5 checksum of file + + bool operator == (const ScenarioIdentifier &other) const + { + return this->scenid == other.scenid && + memcmp(this->md5sum, other.md5sum, sizeof(this->md5sum)) == 0; + } + + bool operator != (const ScenarioIdentifier &other) const + { + return !(*this == other); + } +}; + +/** + * Scanner to find the unique IDs of scenarios + */ +class ScenarioScanner : protected FileScanner, public SmallVector { + bool scanned; ///< Whether we've already scanned +public: + /** Initialise */ + ScenarioScanner() : scanned(false) {} + + /** + * Scan, but only if it's needed. + * @param rescan whether to force scanning even when it's not necessary + */ + void Scan(bool rescan) + { + if (this->scanned && !rescan) return; + + this->FileScanner::Scan(".id", SCENARIO_DIR, true, true); + this->scanned = true; + } + + /* virtual */ bool AddFile(const char *filename, size_t basepath_length) + { + FILE *f = FioFOpenFile(filename, "r"); + if (f == NULL) return false; + + ScenarioIdentifier id; + fscanf(f, "%i", &id.scenid); + FioFCloseFile(f); + + Md5 checksum; + uint8 buffer[1024]; + size_t len, size; + + /* open the scenario file, but first get the name. + * This is safe as we check on extension which + * must always exist. */ + *strrchr(filename, '.') = '\0'; + f = FioFOpenFile(filename, "rb", SCENARIO_DIR, &size); + if (f == NULL) return false; + + /* calculate md5sum */ + while ((len = fread(buffer, 1, (size > sizeof(buffer)) ? sizeof(buffer) : size, f)) != 0 && size != 0) { + size -= len; + checksum.Append(buffer, len); + } + checksum.Finish(id.md5sum); + + FioFCloseFile(f); + + this->Include(id); + return true; + } +}; + +/** Scanner for scenarios */ +static ScenarioScanner _scanner; + +/** + * Check whether we've got a given scenario based on it's unique ID. + * @param ci the content info to compare it to + * @param md5sum whether to look at the md5sum or the id + * @return true if we've got the scenario + */ +bool HasScenario(const ContentInfo *ci, bool md5sum) +{ + _scanner.Scan(false); + + for (ScenarioIdentifier *id = _scanner.Begin(); id != _scanner.End(); id++) { + if (md5sum ? + (memcmp(id->md5sum, ci->md5sum, sizeof(id->md5sum)) == 0) : + (id->scenid == ci->unique_id)) { + return true; + } + } + + return false; +} + +/** + * Force a (re)scan of the scenarios. + */ +void ScanScenarios() +{ + _scanner.Scan(true); +} + +#endif /* ENABLE_NETWORK */ diff --git a/src/network/network_content.cpp b/src/network/network_content.cpp --- a/src/network/network_content.cpp +++ b/src/network/network_content.cpp @@ -22,6 +22,7 @@ extern bool TarListAddFile(const char *filename); extern bool HasGraphicsSet(const ContentInfo *ci, bool md5sum); +extern bool HasScenario(const ContentInfo *ci, bool md5sum); ClientNetworkContentSocketHandler _network_content_client; /** Wrapper function for the HasProc */ @@ -86,6 +87,11 @@ DEF_CONTENT_RECEIVE_COMMAND(Client, PACK proc = AI::HasAI; break; break; + case CONTENT_TYPE_SCENARIO: + case CONTENT_TYPE_HEIGHTMAP: + proc = HasScenario; + break; + default: break; } @@ -143,6 +149,8 @@ void ClientNetworkContentSocketHandler:: { if (type == CONTENT_TYPE_END) { this->RequestContentList(CONTENT_TYPE_BASE_GRAPHICS); + this->RequestContentList(CONTENT_TYPE_SCENARIO); + this->RequestContentList(CONTENT_TYPE_HEIGHTMAP); this->RequestContentList(CONTENT_TYPE_AI); this->RequestContentList(CONTENT_TYPE_NEWGRF); this->RequestContentList(CONTENT_TYPE_AI_LIBRARY); diff --git a/src/network/network_content_gui.cpp b/src/network/network_content_gui.cpp --- a/src/network/network_content_gui.cpp +++ b/src/network/network_content_gui.cpp @@ -98,6 +98,13 @@ public: InvalidateWindowData(WC_NETWORK_WINDOW, 0, 2); break; + case CONTENT_TYPE_SCENARIO: + case CONTENT_TYPE_HEIGHTMAP: + InvalidateWindowClasses(WC_SAVELOAD); + extern void ScanScenarios(); + ScanScenarios(); + break; + default: break; }