network_content.cpp

Go to the documentation of this file.
00001 /* $Id$ */
00002 
00003 /*
00004  * This file is part of OpenTTD.
00005  * OpenTTD 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, version 2.
00006  * OpenTTD 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.
00007  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
00008  */
00009 
00012 #if defined(ENABLE_NETWORK)
00013 
00014 #include "../stdafx.h"
00015 #include "../rev.h"
00016 #include "../ai/ai.hpp"
00017 #include "../window_func.h"
00018 #include "../gui.h"
00019 #include "../base_media_base.h"
00020 #include "../settings_type.h"
00021 #include "network_content.h"
00022 
00023 #include "table/strings.h"
00024 
00025 #if defined(WITH_ZLIB)
00026 #include <zlib.h>
00027 #endif
00028 
00029 extern bool HasScenario(const ContentInfo *ci, bool md5sum);
00030 
00032 ClientNetworkContentSocketHandler _network_content_client;
00033 
00035 static bool HasGRFConfig(const ContentInfo *ci, bool md5sum)
00036 {
00037   return FindGRFConfig(BSWAP32(ci->unique_id), md5sum ? FGCM_EXACT : FGCM_ANY, md5sum ? ci->md5sum : NULL) != NULL;
00038 }
00039 
00047 typedef bool (*HasProc)(const ContentInfo *ci, bool md5sum);
00048 
00049 bool ClientNetworkContentSocketHandler::Receive_SERVER_INFO(Packet *p)
00050 {
00051   ContentInfo *ci = new ContentInfo();
00052   ci->type     = (ContentType)p->Recv_uint8();
00053   ci->id       = (ContentID)p->Recv_uint32();
00054   ci->filesize = p->Recv_uint32();
00055 
00056   p->Recv_string(ci->name, lengthof(ci->name));
00057   p->Recv_string(ci->version, lengthof(ci->name));
00058   p->Recv_string(ci->url, lengthof(ci->url));
00059   p->Recv_string(ci->description, lengthof(ci->description),  true);
00060 
00061   ci->unique_id = p->Recv_uint32();
00062   for (uint j = 0; j < sizeof(ci->md5sum); j++) {
00063     ci->md5sum[j] = p->Recv_uint8();
00064   }
00065 
00066   ci->dependency_count = p->Recv_uint8();
00067   ci->dependencies = MallocT<ContentID>(ci->dependency_count);
00068   for (uint i = 0; i < ci->dependency_count; i++) ci->dependencies[i] = (ContentID)p->Recv_uint32();
00069 
00070   ci->tag_count = p->Recv_uint8();
00071   ci->tags = MallocT<char[32]>(ci->tag_count);
00072   for (uint i = 0; i < ci->tag_count; i++) p->Recv_string(ci->tags[i], lengthof(*ci->tags));
00073 
00074   if (!ci->IsValid()) {
00075     delete ci;
00076     this->Close();
00077     return false;
00078   }
00079 
00080   /* Find the appropriate check function */
00081   HasProc proc = NULL;
00082   switch (ci->type) {
00083     case CONTENT_TYPE_NEWGRF:
00084       proc = HasGRFConfig;
00085       break;
00086 
00087     case CONTENT_TYPE_BASE_GRAPHICS:
00088       proc = BaseGraphics::HasSet;
00089       break;
00090 
00091     case CONTENT_TYPE_BASE_MUSIC:
00092       proc = BaseMusic::HasSet;
00093       break;
00094 
00095     case CONTENT_TYPE_BASE_SOUNDS:
00096       proc = BaseSounds::HasSet;
00097       break;
00098 
00099     case CONTENT_TYPE_AI:
00100     case CONTENT_TYPE_AI_LIBRARY:
00101       proc = AI::HasAI; break;
00102       break;
00103 
00104     case CONTENT_TYPE_SCENARIO:
00105     case CONTENT_TYPE_HEIGHTMAP:
00106       proc = HasScenario;
00107       break;
00108 
00109     default:
00110       break;
00111   }
00112 
00113   if (proc != NULL) {
00114     if (proc(ci, true)) {
00115       ci->state = ContentInfo::ALREADY_HERE;
00116     } else {
00117       ci->state = ContentInfo::UNSELECTED;
00118       if (proc(ci, false)) ci->upgrade = true;
00119     }
00120   } else {
00121     ci->state = ContentInfo::UNSELECTED;
00122   }
00123 
00124   /* Something we don't have and has filesize 0 does not exist in te system */
00125   if (ci->state == ContentInfo::UNSELECTED && ci->filesize == 0) ci->state = ContentInfo::DOES_NOT_EXIST;
00126 
00127   /* Do we already have a stub for this? */
00128   for (ContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) {
00129     ContentInfo *ici = *iter;
00130     if (ici->type == ci->type && ici->unique_id == ci->unique_id &&
00131         memcmp(ci->md5sum, ici->md5sum, sizeof(ci->md5sum)) == 0) {
00132       /* Preserve the name if possible */
00133       if (StrEmpty(ci->name)) strecpy(ci->name, ici->name, lastof(ci->name));
00134       if (ici->IsSelected()) ci->state = ici->state;
00135 
00136       /*
00137        * As ici might be selected by the content window we cannot delete that.
00138        * However, we want to keep most of the values of ci, except the values
00139        * we (just) already preserved.
00140        * So transfer data and ownership of allocated memory from ci to ici.
00141        */
00142       ici->TransferFrom(ci);
00143       delete ci;
00144 
00145       this->OnReceiveContentInfo(ici);
00146       return true;
00147     }
00148   }
00149 
00150   /* Missing content info? Don't list it */
00151   if (ci->filesize == 0) {
00152     delete ci;
00153     return true;
00154   }
00155 
00156   *this->infos.Append() = ci;
00157 
00158   /* Incoming data means that we might need to reconsider dependencies */
00159   for (ContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) {
00160     this->CheckDependencyState(*iter);
00161   }
00162 
00163   this->OnReceiveContentInfo(ci);
00164 
00165   return true;
00166 }
00167 
00172 void ClientNetworkContentSocketHandler::RequestContentList(ContentType type)
00173 {
00174   if (type == CONTENT_TYPE_END) {
00175     this->RequestContentList(CONTENT_TYPE_BASE_GRAPHICS);
00176     this->RequestContentList(CONTENT_TYPE_BASE_MUSIC);
00177     this->RequestContentList(CONTENT_TYPE_BASE_SOUNDS);
00178     this->RequestContentList(CONTENT_TYPE_SCENARIO);
00179     this->RequestContentList(CONTENT_TYPE_HEIGHTMAP);
00180 #ifdef ENABLE_AI
00181     this->RequestContentList(CONTENT_TYPE_AI);
00182     this->RequestContentList(CONTENT_TYPE_AI_LIBRARY);
00183 #endif /* ENABLE_AI */
00184     this->RequestContentList(CONTENT_TYPE_NEWGRF);
00185     return;
00186   }
00187 
00188   this->Connect();
00189 
00190   Packet *p = new Packet(PACKET_CONTENT_CLIENT_INFO_LIST);
00191   p->Send_uint8 ((byte)type);
00192   p->Send_uint32(_openttd_newgrf_version);
00193 
00194   this->SendPacket(p);
00195 }
00196 
00202 void ClientNetworkContentSocketHandler::RequestContentList(uint count, const ContentID *content_ids)
00203 {
00204   this->Connect();
00205 
00206   while (count > 0) {
00207     /* We can "only" send a limited number of IDs in a single packet.
00208      * A packet begins with the packet size and a byte for the type.
00209      * Then this packet adds a byte for the content type and a uint16
00210      * for the count in this packet. The rest of the packet can be
00211      * used for the IDs. */
00212     uint p_count = min(count, (SEND_MTU - sizeof(PacketSize) - sizeof(byte) - sizeof(byte) - sizeof(uint16)) / sizeof(uint32));
00213 
00214     Packet *p = new Packet(PACKET_CONTENT_CLIENT_INFO_ID);
00215     p->Send_uint16(p_count);
00216 
00217     for (uint i = 0; i < p_count; i++) {
00218       p->Send_uint32(content_ids[i]);
00219     }
00220 
00221     this->SendPacket(p);
00222     count -= p_count;
00223     content_ids += p_count;
00224   }
00225 }
00226 
00232 void ClientNetworkContentSocketHandler::RequestContentList(ContentVector *cv, bool send_md5sum)
00233 {
00234   if (cv == NULL) return;
00235 
00236   this->Connect();
00237 
00238   /* 20 is sizeof(uint32) + sizeof(md5sum (byte[16])) */
00239   assert(cv->Length() < 255);
00240   assert(cv->Length() < (SEND_MTU - sizeof(PacketSize) - sizeof(byte) - sizeof(uint8)) / (send_md5sum ? 20 : sizeof(uint32)));
00241 
00242   Packet *p = new Packet(send_md5sum ? PACKET_CONTENT_CLIENT_INFO_EXTID_MD5 : PACKET_CONTENT_CLIENT_INFO_EXTID);
00243   p->Send_uint8(cv->Length());
00244 
00245   for (ContentIterator iter = cv->Begin(); iter != cv->End(); iter++) {
00246     const ContentInfo *ci = *iter;
00247     p->Send_uint8((byte)ci->type);
00248     p->Send_uint32(ci->unique_id);
00249     if (!send_md5sum) continue;
00250 
00251     for (uint j = 0; j < sizeof(ci->md5sum); j++) {
00252       p->Send_uint8(ci->md5sum[j]);
00253     }
00254   }
00255 
00256   this->SendPacket(p);
00257 
00258   for (ContentIterator iter = cv->Begin(); iter != cv->End(); iter++) {
00259     ContentInfo *ci = *iter;
00260     bool found = false;
00261     for (ContentIterator iter2 = this->infos.Begin(); iter2 != this->infos.End(); iter2++) {
00262       ContentInfo *ci2 = *iter2;
00263       if (ci->type == ci2->type && ci->unique_id == ci2->unique_id &&
00264           (!send_md5sum || memcmp(ci->md5sum, ci2->md5sum, sizeof(ci->md5sum)) == 0)) {
00265         found = true;
00266         break;
00267       }
00268     }
00269     if (!found) {
00270       *this->infos.Append() = ci;
00271     } else {
00272       delete ci;
00273     }
00274   }
00275 }
00276 
00283 void ClientNetworkContentSocketHandler::DownloadSelectedContent(uint &files, uint &bytes, bool fallback)
00284 {
00285   bytes = 0;
00286 
00287   ContentIDList content;
00288   for (ContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) {
00289     const ContentInfo *ci = *iter;
00290     if (!ci->IsSelected() || ci->state == ContentInfo::ALREADY_HERE) continue;
00291 
00292     *content.Append() = ci->id;
00293     bytes += ci->filesize;
00294   }
00295 
00296   files = content.Length();
00297 
00298   /* If there's nothing to download, do nothing. */
00299   if (files == 0) return;
00300 
00301   if (_settings_client.network.no_http_content_downloads || fallback) {
00302     this->DownloadSelectedContentFallback(content);
00303   } else {
00304     this->DownloadSelectedContentHTTP(content);
00305   }
00306 }
00307 
00312 void ClientNetworkContentSocketHandler::DownloadSelectedContentHTTP(const ContentIDList &content)
00313 {
00314   uint count = content.Length();
00315 
00316   /* Allocate memory for the whole request.
00317    * Requests are "id\nid\n..." (as strings), so assume the maximum ID,
00318    * which is uint32 so 10 characters long. Then the newlines and
00319    * multiply that all with the count and then add the '\0'. */
00320   uint bytes = (10 + 1) * count + 1;
00321   char *content_request = MallocT<char>(bytes);
00322   const char *lastof = content_request + bytes - 1;
00323 
00324   char *p = content_request;
00325   for (const ContentID *id = content.Begin(); id != content.End(); id++) {
00326     p += seprintf(p, lastof, "%d\n", *id);
00327   }
00328 
00329   this->http_response_index = -1;
00330 
00331   NetworkAddress address(NETWORK_CONTENT_MIRROR_HOST, NETWORK_CONTENT_MIRROR_PORT);
00332   new NetworkHTTPContentConnecter(address, this, NETWORK_CONTENT_MIRROR_URL, content_request);
00333   /* NetworkHTTPContentConnecter takes over freeing of content_request! */
00334 }
00335 
00340 void ClientNetworkContentSocketHandler::DownloadSelectedContentFallback(const ContentIDList &content)
00341 {
00342   uint count = content.Length();
00343   const ContentID *content_ids = content.Begin();
00344   this->Connect();
00345 
00346   while (count > 0) {
00347     /* We can "only" send a limited number of IDs in a single packet.
00348      * A packet begins with the packet size and a byte for the type.
00349      * Then this packet adds a uint16 for the count in this packet.
00350      * The rest of the packet can be used for the IDs. */
00351     uint p_count = min(count, (SEND_MTU - sizeof(PacketSize) - sizeof(byte) - sizeof(uint16)) / sizeof(uint32));
00352 
00353     Packet *p = new Packet(PACKET_CONTENT_CLIENT_CONTENT);
00354     p->Send_uint16(p_count);
00355 
00356     for (uint i = 0; i < p_count; i++) {
00357       p->Send_uint32(content_ids[i]);
00358     }
00359 
00360     this->SendPacket(p);
00361     count -= p_count;
00362     content_ids += p_count;
00363   }
00364 }
00365 
00373 static char *GetFullFilename(const ContentInfo *ci, bool compressed)
00374 {
00375   Subdirectory dir;
00376   switch (ci->type) {
00377     default: return NULL;
00378     case CONTENT_TYPE_BASE_GRAPHICS: dir = DATA_DIR;       break;
00379     case CONTENT_TYPE_BASE_MUSIC:    dir = GM_DIR;         break;
00380     case CONTENT_TYPE_BASE_SOUNDS:   dir = DATA_DIR;       break;
00381     case CONTENT_TYPE_NEWGRF:        dir = DATA_DIR;       break;
00382     case CONTENT_TYPE_AI:            dir = AI_DIR;         break;
00383     case CONTENT_TYPE_AI_LIBRARY:    dir = AI_LIBRARY_DIR; break;
00384     case CONTENT_TYPE_SCENARIO:      dir = SCENARIO_DIR;   break;
00385     case CONTENT_TYPE_HEIGHTMAP:     dir = HEIGHTMAP_DIR;  break;
00386   }
00387 
00388   static char buf[MAX_PATH];
00389   FioGetFullPath(buf, lengthof(buf), SP_AUTODOWNLOAD_DIR, dir, ci->filename);
00390   strecat(buf, compressed ? ".tar.gz" : ".tar", lastof(buf));
00391 
00392   return buf;
00393 }
00394 
00400 static bool GunzipFile(const ContentInfo *ci)
00401 {
00402 #if defined(WITH_ZLIB)
00403   bool ret = true;
00404   FILE *ftmp = fopen(GetFullFilename(ci, true), "rb");
00405   gzFile fin = gzdopen(fileno(ftmp), "rb");
00406   FILE *fout = fopen(GetFullFilename(ci, false), "wb");
00407 
00408   if (fin == NULL || fout == NULL) {
00409     ret = false;
00410   } else {
00411     byte buff[8192];
00412     for (;;) {
00413       int read = gzread(fin, buff, sizeof(buff));
00414       if (read == 0) {
00415         /* If gzread() returns 0, either the end-of-file has been
00416          * reached or an underlying read error has occurred.
00417          *
00418          * gzeof() can't be used, because:
00419          * 1.2.5 - it is safe, 1 means 'everything was OK'
00420          * 1.2.3.5, 1.2.4 - 0 or 1 is returned 'randomly'
00421          * 1.2.3.3 - 1 is returned for truncated archive
00422          *
00423          * So we use gzerror(). When proper end of archive
00424          * has been reached, then:
00425          * errnum == Z_STREAM_END in 1.2.3.3,
00426          * errnum == 0 in 1.2.4 and 1.2.5 */
00427         int errnum;
00428         gzerror(fin, &errnum);
00429         if (errnum != 0 && errnum != Z_STREAM_END) ret = false;
00430         break;
00431       }
00432       if (read < 0 || (size_t)read != fwrite(buff, 1, read, fout)) {
00433         /* If gzread() returns -1, there was an error in archive */
00434         ret = false;
00435         break;
00436       }
00437       /* DO NOT DO THIS! It will fail to detect broken archive with 1.2.3.3!
00438        * if (read < sizeof(buff)) break; */
00439     }
00440   }
00441 
00442   if (fin != NULL) {
00443     /* Closes ftmp too! */
00444     gzclose(fin);
00445   } else if (ftmp != NULL) {
00446     /* In case the gz stream was opened correctly this will
00447      * be closed by gzclose. */
00448     fclose(ftmp);
00449   }
00450   if (fout != NULL) fclose(fout);
00451 
00452   return ret;
00453 #else
00454   NOT_REACHED();
00455 #endif /* defined(WITH_ZLIB) */
00456 }
00457 
00458 bool ClientNetworkContentSocketHandler::Receive_SERVER_CONTENT(Packet *p)
00459 {
00460   if (this->curFile == NULL) {
00461     delete this->curInfo;
00462     /* When we haven't opened a file this must be our first packet with metadata. */
00463     this->curInfo = new ContentInfo;
00464     this->curInfo->type     = (ContentType)p->Recv_uint8();
00465     this->curInfo->id       = (ContentID)p->Recv_uint32();
00466     this->curInfo->filesize = p->Recv_uint32();
00467     p->Recv_string(this->curInfo->filename, lengthof(this->curInfo->filename));
00468 
00469     if (!this->BeforeDownload()) {
00470       this->Close();
00471       return false;
00472     }
00473   } else {
00474     /* We have a file opened, thus are downloading internal content */
00475     size_t toRead = (size_t)(p->size - p->pos);
00476     if (fwrite(p->buffer + p->pos, 1, toRead, this->curFile) != toRead) {
00477       DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0);
00478       ShowErrorMessage(STR_CONTENT_ERROR_COULD_NOT_DOWNLOAD, STR_CONTENT_ERROR_COULD_NOT_DOWNLOAD_FILE_NOT_WRITABLE, WL_ERROR);
00479       this->Close();
00480       fclose(this->curFile);
00481       this->curFile = NULL;
00482 
00483       return false;
00484     }
00485 
00486     this->OnDownloadProgress(this->curInfo, (int)toRead);
00487 
00488     if (toRead == 0) this->AfterDownload();
00489   }
00490 
00491   return true;
00492 }
00493 
00498 bool ClientNetworkContentSocketHandler::BeforeDownload()
00499 {
00500   if (!this->curInfo->IsValid()) {
00501     delete this->curInfo;
00502     this->curInfo = NULL;
00503     return false;
00504   }
00505 
00506   if (this->curInfo->filesize != 0) {
00507     /* The filesize is > 0, so we are going to download it */
00508     const char *filename = GetFullFilename(this->curInfo, true);
00509     if (filename == NULL || (this->curFile = fopen(filename, "wb")) == NULL) {
00510       /* Unless that fails ofcourse... */
00511       DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0);
00512       ShowErrorMessage(STR_CONTENT_ERROR_COULD_NOT_DOWNLOAD, STR_CONTENT_ERROR_COULD_NOT_DOWNLOAD_FILE_NOT_WRITABLE, WL_ERROR);
00513       return false;
00514     }
00515   }
00516   return true;
00517 }
00518 
00523 void ClientNetworkContentSocketHandler::AfterDownload()
00524 {
00525   /* We read nothing; that's our marker for end-of-stream.
00526    * Now gunzip the tar and make it known. */
00527   fclose(this->curFile);
00528   this->curFile = NULL;
00529 
00530   if (GunzipFile(this->curInfo)) {
00531     unlink(GetFullFilename(this->curInfo, true));
00532 
00533     TarScanner ts;
00534     ts.AddFile(GetFullFilename(this->curInfo, false), 0);
00535 
00536     if (this->curInfo->type == CONTENT_TYPE_BASE_MUSIC) {
00537       /* Music can't be in a tar. So extract the tar! */
00538       ExtractTar(GetFullFilename(this->curInfo, false));
00539       unlink(GetFullFilename(this->curInfo, false));
00540     }
00541 
00542     this->OnDownloadComplete(this->curInfo->id);
00543   } else {
00544     ShowErrorMessage(STR_CONTENT_ERROR_COULD_NOT_EXTRACT, INVALID_STRING_ID, WL_ERROR);
00545   }
00546 }
00547 
00548 /* Also called to just clean up the mess. */
00549 void ClientNetworkContentSocketHandler::OnFailure()
00550 {
00551   /* If we fail, download the rest via the 'old' system. */
00552   uint files, bytes;
00553   this->DownloadSelectedContent(files, bytes, true);
00554 
00555   this->http_response.Reset();
00556   this->http_response_index = -2;
00557 
00558   if (this->curFile != NULL) {
00559     /* Revert the download progress when we are going for the old system. */
00560     long size = ftell(this->curFile);
00561     if (size > 0) this->OnDownloadProgress(this->curInfo, (int)-size);
00562 
00563     fclose(this->curFile);
00564     this->curFile = NULL;
00565   }
00566 }
00567 
00568 void ClientNetworkContentSocketHandler::OnReceiveData(const char *data, size_t length)
00569 {
00570   assert(data == NULL || length != 0);
00571 
00572   /* Ignore any latent data coming from a connection we closed. */
00573   if (this->http_response_index == -2) return;
00574 
00575   if (this->http_response_index == -1) {
00576     if (data != NULL) {
00577       /* Append the rest of the response. */
00578       memcpy(this->http_response.Append((uint)length), data, length);
00579       return;
00580     } else {
00581       /* Make sure the response is properly terminated. */
00582       *this->http_response.Append() = '\0';
00583 
00584       /* And prepare for receiving the rest of the data. */
00585       this->http_response_index = 0;
00586     }
00587   }
00588 
00589   if (data != NULL) {
00590     /* We have data, so write it to the file. */
00591     if (fwrite(data, 1, length, this->curFile) != length) {
00592       /* Writing failed somehow, let try via the old method. */
00593       this->OnFailure();
00594     } else {
00595       /* Just received the data. */
00596       this->OnDownloadProgress(this->curInfo, (int)length);
00597     }
00598     /* Nothing more to do now. */
00599     return;
00600   }
00601 
00602   if (this->curFile != NULL) {
00603     /* We've finished downloading a file. */
00604     this->AfterDownload();
00605   }
00606 
00607   if ((uint)this->http_response_index >= this->http_response.Length()) {
00608     /* It's not a real failure, but if there's
00609      * nothing more to download it helps with
00610      * cleaning up the stuff we allocated. */
00611     this->OnFailure();
00612     return;
00613   }
00614 
00615   delete this->curInfo;
00616   /* When we haven't opened a file this must be our first packet with metadata. */
00617   this->curInfo = new ContentInfo;
00618 
00620 #define check_not_null(p) { if ((p) == NULL) { this->OnFailure(); return; } }
00621 
00622 #define check_and_terminate(p) { check_not_null(p); *(p) = '\0'; }
00623 
00624   for (;;) {
00625     char *str = this->http_response.Begin() + this->http_response_index;
00626     char *p = strchr(str, '\n');
00627     check_and_terminate(p);
00628 
00629     /* Update the index for the next one */
00630     this->http_response_index += (int)strlen(str) + 1;
00631 
00632     /* Read the ID */
00633     p = strchr(str, ',');
00634     check_and_terminate(p);
00635     this->curInfo->id = (ContentID)atoi(str);
00636 
00637     /* Read the type */
00638     str = p + 1;
00639     p = strchr(str, ',');
00640     check_and_terminate(p);
00641     this->curInfo->type = (ContentType)atoi(str);
00642 
00643     /* Read the file size */
00644     str = p + 1;
00645     p = strchr(str, ',');
00646     check_and_terminate(p);
00647     this->curInfo->filesize = atoi(str);
00648 
00649     /* Read the URL */
00650     str = p + 1;
00651     /* Is it a fallback URL? If so, just continue with the next one. */
00652     if (strncmp(str, "ottd", 4) == 0) {
00653       if ((uint)this->http_response_index >= this->http_response.Length()) {
00654         /* Have we gone through all lines? */
00655         this->OnFailure();
00656         return;
00657       }
00658       continue;
00659     }
00660 
00661     p = strrchr(str, '/');
00662     check_not_null(p);
00663     p++; // Start after the '/'
00664 
00665     char tmp[MAX_PATH];
00666     if (strecpy(tmp, p, lastof(tmp)) == lastof(tmp)) {
00667       this->OnFailure();
00668       return;
00669     }
00670     /* Remove the extension from the string. */
00671     for (uint i = 0; i < 2; i++) {
00672       p = strrchr(tmp, '.');
00673       check_and_terminate(p);
00674     }
00675 
00676     /* Copy the string, without extension, to the filename. */
00677     strecpy(this->curInfo->filename, tmp, lastof(this->curInfo->filename));
00678 
00679     /* Request the next file. */
00680     if (!this->BeforeDownload()) {
00681       this->OnFailure();
00682       return;
00683     }
00684 
00685     NetworkHTTPSocketHandler::Connect(str, this);
00686     return;
00687   }
00688 
00689 #undef check
00690 #undef check_and_terminate
00691 }
00692 
00696 ClientNetworkContentSocketHandler::ClientNetworkContentSocketHandler() :
00697   NetworkContentSocketHandler(),
00698   http_response_index(-2),
00699   curFile(NULL),
00700   curInfo(NULL),
00701   isConnecting(false)
00702 {
00703 }
00704 
00706 ClientNetworkContentSocketHandler::~ClientNetworkContentSocketHandler()
00707 {
00708   delete this->curInfo;
00709   if (this->curFile != NULL) fclose(this->curFile);
00710 
00711   for (ContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) delete *iter;
00712 }
00713 
00715 class NetworkContentConnecter : TCPConnecter {
00716 public:
00721   NetworkContentConnecter(const NetworkAddress &address) : TCPConnecter(address) {}
00722 
00723   virtual void OnFailure()
00724   {
00725     _network_content_client.isConnecting = false;
00726     _network_content_client.OnConnect(false);
00727   }
00728 
00729   virtual void OnConnect(SOCKET s)
00730   {
00731     assert(_network_content_client.sock == INVALID_SOCKET);
00732     _network_content_client.isConnecting = false;
00733     _network_content_client.sock = s;
00734     _network_content_client.Reopen();
00735     _network_content_client.OnConnect(true);
00736   }
00737 };
00738 
00742 void ClientNetworkContentSocketHandler::Connect()
00743 {
00744   this->lastActivity = _realtime_tick;
00745 
00746   if (this->sock != INVALID_SOCKET || this->isConnecting) return;
00747   this->isConnecting = true;
00748   new NetworkContentConnecter(NetworkAddress(NETWORK_CONTENT_SERVER_HOST, NETWORK_CONTENT_SERVER_PORT, AF_UNSPEC));
00749 }
00750 
00754 void ClientNetworkContentSocketHandler::Close()
00755 {
00756   if (this->sock == INVALID_SOCKET) return;
00757   NetworkContentSocketHandler::Close();
00758 
00759   this->OnDisconnect();
00760 }
00761 
00766 void ClientNetworkContentSocketHandler::SendReceive()
00767 {
00768   if (this->sock == INVALID_SOCKET || this->isConnecting) return;
00769 
00770   if (this->lastActivity + IDLE_TIMEOUT < _realtime_tick) {
00771     this->Close();
00772     return;
00773   }
00774 
00775   if (this->CanSendReceive()) {
00776     this->ReceivePackets();
00777     this->lastActivity = _realtime_tick;
00778   }
00779 
00780   this->SendPackets();
00781 }
00782 
00787 void ClientNetworkContentSocketHandler::DownloadContentInfo(ContentID cid)
00788 {
00789   /* When we tried to download it already, don't try again */
00790   if (this->requested.Contains(cid)) return;
00791 
00792   *this->requested.Append() = cid;
00793   assert(this->requested.Contains(cid));
00794   this->RequestContentList(1, &cid);
00795 }
00796 
00802 ContentInfo *ClientNetworkContentSocketHandler::GetContent(ContentID cid)
00803 {
00804   for (ContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) {
00805     ContentInfo *ci = *iter;
00806     if (ci->id == cid) return ci;
00807   }
00808   return NULL;
00809 }
00810 
00811 
00816 void ClientNetworkContentSocketHandler::Select(ContentID cid)
00817 {
00818   ContentInfo *ci = this->GetContent(cid);
00819   if (ci == NULL || ci->state != ContentInfo::UNSELECTED) return;
00820 
00821   ci->state = ContentInfo::SELECTED;
00822   this->CheckDependencyState(ci);
00823 }
00824 
00829 void ClientNetworkContentSocketHandler::Unselect(ContentID cid)
00830 {
00831   ContentInfo *ci = this->GetContent(cid);
00832   if (ci == NULL || !ci->IsSelected()) return;
00833 
00834   ci->state = ContentInfo::UNSELECTED;
00835   this->CheckDependencyState(ci);
00836 }
00837 
00839 void ClientNetworkContentSocketHandler::SelectAll()
00840 {
00841   for (ContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) {
00842     ContentInfo *ci = *iter;
00843     if (ci->state == ContentInfo::UNSELECTED) {
00844       ci->state = ContentInfo::SELECTED;
00845       this->CheckDependencyState(ci);
00846     }
00847   }
00848 }
00849 
00851 void ClientNetworkContentSocketHandler::SelectUpgrade()
00852 {
00853   for (ContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) {
00854     ContentInfo *ci = *iter;
00855     if (ci->state == ContentInfo::UNSELECTED && ci->upgrade) {
00856       ci->state = ContentInfo::SELECTED;
00857       this->CheckDependencyState(ci);
00858     }
00859   }
00860 }
00861 
00863 void ClientNetworkContentSocketHandler::UnselectAll()
00864 {
00865   for (ContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) {
00866     ContentInfo *ci = *iter;
00867     if (ci->IsSelected() && ci->state != ContentInfo::ALREADY_HERE) ci->state = ContentInfo::UNSELECTED;
00868   }
00869 }
00870 
00872 void ClientNetworkContentSocketHandler::ToggleSelectedState(const ContentInfo *ci)
00873 {
00874   switch (ci->state) {
00875     case ContentInfo::SELECTED:
00876     case ContentInfo::AUTOSELECTED:
00877       this->Unselect(ci->id);
00878       break;
00879 
00880     case ContentInfo::UNSELECTED:
00881       this->Select(ci->id);
00882       break;
00883 
00884     default:
00885       break;
00886   }
00887 }
00888 
00894 void ClientNetworkContentSocketHandler::ReverseLookupDependency(ConstContentVector &parents, const ContentInfo *child) const
00895 {
00896   for (ConstContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) {
00897     const ContentInfo *ci = *iter;
00898     if (ci == child) continue;
00899 
00900     for (uint i = 0; i < ci->dependency_count; i++) {
00901       if (ci->dependencies[i] == child->id) {
00902         *parents.Append() = ci;
00903         break;
00904       }
00905     }
00906   }
00907 }
00908 
00914 void ClientNetworkContentSocketHandler::ReverseLookupTreeDependency(ConstContentVector &tree, const ContentInfo *child) const
00915 {
00916   *tree.Append() = child;
00917 
00918   /* First find all direct parents. We can't use the "normal" iterator as
00919    * we are including stuff into the vector and as such the vector's data
00920    * store can be reallocated (and thus move), which means out iterating
00921    * pointer gets invalid. So fall back to the indices. */
00922   for (uint i = 0; i < tree.Length(); i++) {
00923     ConstContentVector parents;
00924     this->ReverseLookupDependency(parents, tree[i]);
00925 
00926     for (ConstContentIterator piter = parents.Begin(); piter != parents.End(); piter++) {
00927       tree.Include(*piter);
00928     }
00929   }
00930 }
00931 
00936 void ClientNetworkContentSocketHandler::CheckDependencyState(ContentInfo *ci)
00937 {
00938   if (ci->IsSelected() || ci->state == ContentInfo::ALREADY_HERE) {
00939     /* Selection is easy; just walk all children and set the
00940      * autoselected state. That way we can see what we automatically
00941      * selected and thus can unselect when a dependency is removed. */
00942     for (uint i = 0; i < ci->dependency_count; i++) {
00943       ContentInfo *c = this->GetContent(ci->dependencies[i]);
00944       if (c == NULL) {
00945         this->DownloadContentInfo(ci->dependencies[i]);
00946       } else if (c->state == ContentInfo::UNSELECTED) {
00947         c->state = ContentInfo::AUTOSELECTED;
00948         this->CheckDependencyState(c);
00949       }
00950     }
00951     return;
00952   }
00953 
00954   if (ci->state != ContentInfo::UNSELECTED) return;
00955 
00956   /* For unselection we need to find the parents of us. We need to
00957    * unselect them. After that we unselect all children that we
00958    * depend on and are not used as dependency for us, but only when
00959    * we automatically selected them. */
00960   ConstContentVector parents;
00961   this->ReverseLookupDependency(parents, ci);
00962   for (ConstContentIterator iter = parents.Begin(); iter != parents.End(); iter++) {
00963     const ContentInfo *c = *iter;
00964     if (!c->IsSelected()) continue;
00965 
00966     this->Unselect(c->id);
00967   }
00968 
00969   for (uint i = 0; i < ci->dependency_count; i++) {
00970     const ContentInfo *c = this->GetContent(ci->dependencies[i]);
00971     if (c == NULL) {
00972       DownloadContentInfo(ci->dependencies[i]);
00973       continue;
00974     }
00975     if (c->state != ContentInfo::AUTOSELECTED) continue;
00976 
00977     /* Only unselect when WE are the only parent. */
00978     parents.Clear();
00979     this->ReverseLookupDependency(parents, c);
00980 
00981     /* First check whether anything depends on us */
00982     int sel_count = 0;
00983     bool force_selection = false;
00984     for (ConstContentIterator iter = parents.Begin(); iter != parents.End(); iter++) {
00985       if ((*iter)->IsSelected()) sel_count++;
00986       if ((*iter)->state == ContentInfo::SELECTED) force_selection = true;
00987     }
00988     if (sel_count == 0) {
00989       /* Nothing depends on us */
00990       this->Unselect(c->id);
00991       continue;
00992     }
00993     /* Something manually selected depends directly on us */
00994     if (force_selection) continue;
00995 
00996     /* "Flood" search to find all items in the dependency graph*/
00997     parents.Clear();
00998     this->ReverseLookupTreeDependency(parents, c);
00999 
01000     /* Is there anything that is "force" selected?, if so... we're done. */
01001     for (ConstContentIterator iter = parents.Begin(); iter != parents.End(); iter++) {
01002       if ((*iter)->state != ContentInfo::SELECTED) continue;
01003 
01004       force_selection = true;
01005       break;
01006     }
01007 
01008     /* So something depended directly on us */
01009     if (force_selection) continue;
01010 
01011     /* Nothing depends on us, mark the whole graph as unselected.
01012      * After that's done run over them once again to test their children
01013      * to unselect. Don't do it immediately because it'll do exactly what
01014      * we're doing now. */
01015     for (ConstContentIterator iter = parents.Begin(); iter != parents.End(); iter++) {
01016       const ContentInfo *c = *iter;
01017       if (c->state == ContentInfo::AUTOSELECTED) this->Unselect(c->id);
01018     }
01019     for (ConstContentIterator iter = parents.Begin(); iter != parents.End(); iter++) {
01020       this->CheckDependencyState(this->GetContent((*iter)->id));
01021     }
01022   }
01023 }
01024 
01026 void ClientNetworkContentSocketHandler::Clear()
01027 {
01028   for (ContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) delete *iter;
01029 
01030   this->infos.Clear();
01031   this->requested.Clear();
01032 }
01033 
01034 /*** CALLBACK ***/
01035 
01036 void ClientNetworkContentSocketHandler::OnConnect(bool success)
01037 {
01038   for (ContentCallback **iter = this->callbacks.Begin(); iter != this->callbacks.End(); /* nothing */) {
01039     ContentCallback *cb = *iter;
01040     cb->OnConnect(success);
01041     if (iter != this->callbacks.End() && *iter == cb) iter++;
01042   }
01043 }
01044 
01045 void ClientNetworkContentSocketHandler::OnDisconnect()
01046 {
01047   for (ContentCallback **iter = this->callbacks.Begin(); iter != this->callbacks.End(); /* nothing */) {
01048     ContentCallback *cb = *iter;
01049     cb->OnDisconnect();
01050     if (iter != this->callbacks.End() && *iter == cb) iter++;
01051   }
01052 }
01053 
01054 void ClientNetworkContentSocketHandler::OnReceiveContentInfo(const ContentInfo *ci)
01055 {
01056   for (ContentCallback **iter = this->callbacks.Begin(); iter != this->callbacks.End(); /* nothing */) {
01057     ContentCallback *cb = *iter;
01058     cb->OnReceiveContentInfo(ci);
01059     if (iter != this->callbacks.End() && *iter == cb) iter++;
01060   }
01061 }
01062 
01063 void ClientNetworkContentSocketHandler::OnDownloadProgress(const ContentInfo *ci, int bytes)
01064 {
01065   for (ContentCallback **iter = this->callbacks.Begin(); iter != this->callbacks.End(); /* nothing */) {
01066     ContentCallback *cb = *iter;
01067     cb->OnDownloadProgress(ci, bytes);
01068     if (iter != this->callbacks.End() && *iter == cb) iter++;
01069   }
01070 }
01071 
01072 void ClientNetworkContentSocketHandler::OnDownloadComplete(ContentID cid)
01073 {
01074   ContentInfo *ci = this->GetContent(cid);
01075   if (ci != NULL) {
01076     ci->state = ContentInfo::ALREADY_HERE;
01077   }
01078 
01079   for (ContentCallback **iter = this->callbacks.Begin(); iter != this->callbacks.End(); /* nothing */) {
01080     ContentCallback *cb = *iter;
01081     cb->OnDownloadComplete(cid);
01082     if (iter != this->callbacks.End() && *iter == cb) iter++;
01083   }
01084 }
01085 
01086 #endif /* ENABLE_NETWORK */

Generated on Sun Jun 5 04:19:58 2011 for OpenTTD by  doxygen 1.6.1