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

Generated on Wed Apr 13 00:47:49 2011 for OpenTTD by  doxygen 1.6.1