# network_content.cpp

Go to the documentation of this file.

    00001 /* $Id: network_content.cpp 18638 2009-12-26 09:38:41Z rubidium $ */
    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 "../variables.h"
    00020 #include "../base_media_base.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 TarListAddFile(const char *filename);
    00030 extern bool HasScenario(const ContentInfo *ci, bool md5sum);
    00031 ClientNetworkContentSocketHandler _network_content_client;
    00032
    00034 static bool HasGRFConfig(const ContentInfo *ci, bool md5sum)
    00035 {
    00036   return FindGRFConfig(BSWAP32(ci->unique_id), md5sum ? ci->md5sum : NULL) != NULL;
    00037 }
    00038
    00046 typedef bool (*HasProc)(const ContentInfo *ci, bool md5sum);
    00047
    00048 DEF_CONTENT_RECEIVE_COMMAND(Client, PACKET_CONTENT_SERVER_INFO)
    00049 {
    00050   ContentInfo *ci = new ContentInfo();
    00051   ci->type     = (ContentType)p->Recv_uint8();
    00052   ci->id       = (ContentID)p->Recv_uint32();
    00053   ci->filesize = p->Recv_uint32();
    00054
    00055   p->Recv_string(ci->name, lengthof(ci->name));
    00056   p->Recv_string(ci->version, lengthof(ci->name));
    00057   p->Recv_string(ci->url, lengthof(ci->url));
    00058   p->Recv_string(ci->description, lengthof(ci->description),  true);
    00059
    00060   ci->unique_id = p->Recv_uint32();
    00061   for (uint j = 0; j < sizeof(ci->md5sum); j++) {
    00062     ci->md5sum[j] = p->Recv_uint8();
    00063   }
    00064
    00065   ci->dependency_count = p->Recv_uint8();
    00066   ci->dependencies = MallocT<ContentID>(ci->dependency_count);
    00067   for (uint i = 0; i < ci->dependency_count; i++) ci->dependencies[i] = (ContentID)p->Recv_uint32();
    00068
    00069   ci->tag_count = p->Recv_uint8();
    00070   ci->tags = MallocT<char[32]>(ci->tag_count);
    00071   for (uint i = 0; i < ci->tag_count; i++) p->Recv_string(ci->tags[i], lengthof(*ci->tags));
    00072
    00073   if (!ci->IsValid()) {
    00074     delete ci;
    00075     this->Close();
    00076     return false;
    00077   }
    00078
    00079   /* Find the appropriate check function */
    00080   HasProc proc = NULL;
    00081   switch (ci->type) {
    00082     case CONTENT_TYPE_NEWGRF:
    00083       proc = HasGRFConfig;
    00084       break;
    00085
    00086     case CONTENT_TYPE_BASE_GRAPHICS:
    00087       proc = BaseGraphics::HasSet;
    00088       break;
    00089
    00090     case CONTENT_TYPE_BASE_MUSIC:
    00091       proc = BaseMusic::HasSet;
    00092       break;
    00093
    00094     case CONTENT_TYPE_BASE_SOUNDS:
    00095       proc = BaseSounds::HasSet;
    00096       break;
    00097
    00098     case CONTENT_TYPE_AI:
    00099     case CONTENT_TYPE_AI_LIBRARY:
    00100       proc = AI::HasAI; break;
    00101       break;
    00102
    00103     case CONTENT_TYPE_SCENARIO:
    00104     case CONTENT_TYPE_HEIGHTMAP:
    00105       proc = HasScenario;
    00106       break;
    00107
    00108     default:
    00109       break;
    00110   }
    00111
    00112   if (proc != NULL) {
    00113     if (proc(ci, true)) {
    00114       ci->state = ContentInfo::ALREADY_HERE;
    00115     } else {
    00116       ci->state = ContentInfo::UNSELECTED;
    00117       if (proc(ci, false)) ci->upgrade = true;
    00118     }
    00119   } else {
    00120     ci->state = ContentInfo::UNSELECTED;
    00121   }
    00122
    00123   /* Something we don't have and has filesize 0 does not exist in te system */
    00124   if (ci->state == ContentInfo::UNSELECTED && ci->filesize == 0) ci->state = ContentInfo::DOES_NOT_EXIST;
    00125
    00126   /* Do we already have a stub for this? */
    00127   for (ContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) {
    00128     ContentInfo *ici = *iter;
    00129     if (ici->type == ci->type && ici->unique_id == ci->unique_id &&
    00130         memcmp(ci->md5sum, ici->md5sum, sizeof(ci->md5sum)) == 0) {
    00131       /* Preserve the name if possible */
    00132       if (StrEmpty(ci->name)) strecpy(ci->name, ici->name, lastof(ci->name));
    00133       if (ici->IsSelected()) ci->state = ici->state;
    00134
    00135       delete ici;
    00136       *iter = ci;
    00137
    00138       this->OnReceiveContentInfo(ci);
    00139       return true;
    00140     }
    00141   }
    00142
    00143   /* Missing content info? Don't list it */
    00144   if (ci->filesize == 0) {
    00145     delete ci;
    00146     return true;
    00147   }
    00148
    00149   *this->infos.Append() = ci;
    00150
    00151   /* Incoming data means that we might need to reconsider dependencies */
    00152   for (ContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) {
    00153     this->CheckDependencyState(*iter);
    00154   }
    00155
    00156   this->OnReceiveContentInfo(ci);
    00157
    00158   return true;
    00159 }
    00160
    00161 void ClientNetworkContentSocketHandler::RequestContentList(ContentType type)
    00162 {
    00163   if (type == CONTENT_TYPE_END) {
    00164     this->RequestContentList(CONTENT_TYPE_BASE_GRAPHICS);
    00165     this->RequestContentList(CONTENT_TYPE_BASE_MUSIC);
    00166     this->RequestContentList(CONTENT_TYPE_BASE_SOUNDS);
    00167     this->RequestContentList(CONTENT_TYPE_SCENARIO);
    00168     this->RequestContentList(CONTENT_TYPE_HEIGHTMAP);
    00169     this->RequestContentList(CONTENT_TYPE_AI);
    00170     this->RequestContentList(CONTENT_TYPE_NEWGRF);
    00171     this->RequestContentList(CONTENT_TYPE_AI_LIBRARY);
    00172     return;
    00173   }
    00174
    00175   this->Connect();
    00176
    00177   Packet *p = new Packet(PACKET_CONTENT_CLIENT_INFO_LIST);
    00178   p->Send_uint8 ((byte)type);
    00179   p->Send_uint32(_openttd_newgrf_version);
    00180
    00181   this->Send_Packet(p);
    00182 }
    00183
    00184 void ClientNetworkContentSocketHandler::RequestContentList(uint count, const ContentID *content_ids)
    00185 {
    00186   this->Connect();
    00187
    00188   while (count > 0) {
    00189     /* We can "only" send a limited number of IDs in a single packet.
    00190      * A packet begins with the packet size and a byte for the type.
    00191      * Then this packet adds a byte for the content type and a uint16
    00192      * for the count in this packet. The rest of the packet can be
    00193      * used for the IDs. */
    00194     uint p_count = min(count, (SEND_MTU - sizeof(PacketSize) - sizeof(byte) - sizeof(byte) - sizeof(uint16)) / sizeof(uint32));
    00195
    00196     Packet *p = new Packet(PACKET_CONTENT_CLIENT_INFO_ID);
    00197     p->Send_uint16(p_count);
    00198
    00199     for (uint i = 0; i < p_count; i++) {
    00200       p->Send_uint32(content_ids[i]);
    00201     }
    00202
    00203     this->Send_Packet(p);
    00204     count -= p_count;
    00205     content_ids += p_count;
    00206   }
    00207 }
    00208
    00209 void ClientNetworkContentSocketHandler::RequestContentList(ContentVector *cv, bool send_md5sum)
    00210 {
    00211   if (cv == NULL) return;
    00212
    00213   this->Connect();
    00214
    00215   /* 20 is sizeof(uint32) + sizeof(md5sum (byte[16])) */
    00216   assert(cv->Length() < 255);
    00217   assert(cv->Length() < (SEND_MTU - sizeof(PacketSize) - sizeof(byte) - sizeof(uint8)) / (send_md5sum ? 20 : sizeof(uint32)));
    00218
    00219   Packet *p = new Packet(send_md5sum ? PACKET_CONTENT_CLIENT_INFO_EXTID_MD5 : PACKET_CONTENT_CLIENT_INFO_EXTID);
    00220   p->Send_uint8(cv->Length());
    00221
    00222   for (ContentIterator iter = cv->Begin(); iter != cv->End(); iter++) {
    00223     const ContentInfo *ci = *iter;
    00224     p->Send_uint8((byte)ci->type);
    00225     p->Send_uint32(ci->unique_id);
    00226     if (!send_md5sum) continue;
    00227
    00228     for (uint j = 0; j < sizeof(ci->md5sum); j++) {
    00229       p->Send_uint8(ci->md5sum[j]);
    00230     }
    00231   }
    00232
    00233   this->Send_Packet(p);
    00234
    00235   for (ContentIterator iter = cv->Begin(); iter != cv->End(); iter++) {
    00236     ContentInfo *ci = *iter;
    00237     bool found = false;
    00238     for (ContentIterator iter2 = this->infos.Begin(); iter2 != this->infos.End(); iter2++) {
    00239       ContentInfo *ci2 = *iter2;
    00240       if (ci->type == ci2->type && ci->unique_id == ci2->unique_id &&
    00241           (!send_md5sum || memcmp(ci->md5sum, ci2->md5sum, sizeof(ci->md5sum)) == 0)) {
    00242         found = true;
    00243         break;
    00244       }
    00245     }
    00246     if (!found) {
    00247       *this->infos.Append() = ci;
    00248     } else {
    00249       delete ci;
    00250     }
    00251   }
    00252 }
    00253
    00254 void ClientNetworkContentSocketHandler::DownloadSelectedContent(uint &files, uint &bytes)
    00255 {
    00256   files = 0;
    00257   bytes = 0;
    00258
    00260   ContentID *ids = MallocT<ContentID>(infos.Length());
    00261   for (ContentIterator iter = infos.Begin(); iter != infos.End(); iter++) {
    00262     const ContentInfo *ci = *iter;
    00263     if (!ci->IsSelected() || ci->state == ContentInfo::ALREADY_HERE) continue;
    00264
    00265     ids[files++] = ci->id;
    00266     bytes += ci->filesize;
    00267   }
    00268
    00269   uint count = files;
    00270   ContentID *content_ids = ids;
    00271   this->Connect();
    00272
    00273   while (count > 0) {
    00274     /* We can "only" send a limited number of IDs in a single packet.
    00275      * A packet begins with the packet size and a byte for the type.
    00276      * Then this packet adds a uint16 for the count in this packet.
    00277      * The rest of the packet can be used for the IDs. */
    00278     uint p_count = min(count, (SEND_MTU - sizeof(PacketSize) - sizeof(byte) - sizeof(uint16)) / sizeof(uint32));
    00279
    00280     Packet *p = new Packet(PACKET_CONTENT_CLIENT_CONTENT);
    00281     p->Send_uint16(p_count);
    00282
    00283     for (uint i = 0; i < p_count; i++) {
    00284       p->Send_uint32(content_ids[i]);
    00285     }
    00286
    00287     this->Send_Packet(p);
    00288     count -= p_count;
    00289     content_ids += p_count;
    00290   }
    00291
    00292   free(ids);
    00293 }
    00294
    00302 static char *GetFullFilename(const ContentInfo *ci, bool compressed)
    00303 {
    00304   Subdirectory dir;
    00305   switch (ci->type) {
    00306     default: return NULL;
    00307     case CONTENT_TYPE_BASE_GRAPHICS: dir = DATA_DIR;       break;
    00308     case CONTENT_TYPE_BASE_MUSIC:    dir = GM_DIR;         break;
    00309     case CONTENT_TYPE_BASE_SOUNDS:   dir = DATA_DIR;       break;
    00310     case CONTENT_TYPE_NEWGRF:        dir = DATA_DIR;       break;
    00311     case CONTENT_TYPE_AI:            dir = AI_DIR;         break;
    00312     case CONTENT_TYPE_AI_LIBRARY:    dir = AI_LIBRARY_DIR; break;
    00313     case CONTENT_TYPE_SCENARIO:      dir = SCENARIO_DIR;   break;
    00314     case CONTENT_TYPE_HEIGHTMAP:     dir = HEIGHTMAP_DIR;  break;
    00315   }
    00316
    00317   static char buf[MAX_PATH];
    00318   FioGetFullPath(buf, lengthof(buf), SP_AUTODOWNLOAD_DIR, dir, ci->filename);
    00319   strecat(buf, compressed ? ".tar.gz" : ".tar", lastof(buf));
    00320
    00321   return buf;
    00322 }
    00323
    00329 static bool GunzipFile(const ContentInfo *ci)
    00330 {
    00331 #if defined(WITH_ZLIB)
    00332   bool ret = true;
    00333   FILE *ftmp = fopen(GetFullFilename(ci, true), "rb");
    00334   gzFile fin = gzdopen(fileno(ftmp), "rb");
    00335   FILE *fout = fopen(GetFullFilename(ci, false), "wb");
    00336
    00337   if (fin == NULL || fout == NULL) {
    00338     ret = false;
    00339     goto exit;
    00340   }
    00341
    00342   byte buff[8192];
    00343   while (!gzeof(fin)) {
    00344     int read = gzread(fin, buff, sizeof(buff));
    00345     if (read < 0 || (size_t)read != fwrite(buff, 1, read, fout)) {
    00346       ret = false;
    00347       break;
    00348     }
    00349   }
    00350
    00351 exit:
    00352   if (fin != NULL) {
    00353     /* Closes ftmp too! */
    00354     gzclose(fin);
    00355   } else if (ftmp != NULL) {
    00356     /* In case the gz stream was opened correctly this will
    00357      * be closed by gzclose. */
    00358     fclose(ftmp);
    00359   }
    00360   if (fout != NULL) fclose(fout);
    00361
    00362   return ret;
    00363 #else
    00364   NOT_REACHED();
    00365 #endif /* defined(WITH_ZLIB) */
    00366 }
    00367
    00368 DEF_CONTENT_RECEIVE_COMMAND(Client, PACKET_CONTENT_SERVER_CONTENT)
    00369 {
    00370   if (this->curFile == NULL) {
    00371     /* When we haven't opened a file this must be our first packet with metadata. */
    00372     this->curInfo = new ContentInfo;
    00373     this->curInfo->type     = (ContentType)p->Recv_uint8();
    00374     this->curInfo->id       = (ContentID)p->Recv_uint32();
    00375     this->curInfo->filesize = p->Recv_uint32();
    00376     p->Recv_string(this->curInfo->filename, lengthof(this->curInfo->filename));
    00377
    00378     if (!this->curInfo->IsValid()) {
    00379       delete this->curInfo;
    00380       this->curInfo = NULL;
    00381       this->Close();
    00382       return false;
    00383     }
    00384
    00385     if (this->curInfo->filesize != 0) {
    00386       /* The filesize is > 0, so we are going to download it */
    00387       const char *filename = GetFullFilename(this->curInfo, true);
    00388       if (filename == NULL) {
    00389         /* Unless that fails ofcourse... */
    00390         DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0);
    00391         ShowErrorMessage(STR_CONTENT_ERROR_COULD_NOT_DOWNLOAD_FILE_NOT_WRITABLE, STR_CONTENT_ERROR_COULD_NOT_DOWNLOAD, 0, 0);
    00392         this->Close();
    00393         return false;
    00394       }
    00395
    00396       this->curFile = fopen(filename, "wb");
    00397     }
    00398   } else {
    00399     /* We have a file opened, thus are downloading internal content */
    00400     size_t toRead = (size_t)(p->size - p->pos);
    00401     if (fwrite(p->buffer + p->pos, 1, toRead, this->curFile) != toRead) {
    00402       DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0);
    00403       ShowErrorMessage(STR_CONTENT_ERROR_COULD_NOT_DOWNLOAD_FILE_NOT_WRITABLE, STR_CONTENT_ERROR_COULD_NOT_DOWNLOAD, 0, 0);
    00404       this->Close();
    00405       fclose(this->curFile);
    00406       this->curFile = NULL;
    00407
    00408       return false;
    00409     }
    00410
    00411     this->OnDownloadProgress(this->curInfo, (uint)toRead);
    00412
    00413     if (toRead == 0) {
    00414       /* We read nothing; that's our marker for end-of-stream.
    00415        * Now gunzip the tar and make it known. */
    00416       fclose(this->curFile);
    00417       this->curFile = NULL;
    00418
    00419       if (GunzipFile(this->curInfo)) {
    00420         unlink(GetFullFilename(this->curInfo, true));
    00421
    00422         TarListAddFile(GetFullFilename(this->curInfo, false));
    00423
    00424         this->OnDownloadComplete(this->curInfo->id);
    00425       } else {
    00426         ShowErrorMessage(STR_CONTENT_ERROR_COULD_NOT_EXTRACT, INVALID_STRING_ID, 0, 0);
    00427       }
    00428     }
    00429   }
    00430
    00431   /* We ended this file, so clean up the mess */
    00432   if (this->curFile == NULL) {
    00433     delete this->curInfo;
    00434     this->curInfo = NULL;
    00435   }
    00436
    00437   return true;
    00438 }
    00439
    00445 ClientNetworkContentSocketHandler::ClientNetworkContentSocketHandler() :
    00446   NetworkContentSocketHandler(),
    00447   curFile(NULL),
    00448   curInfo(NULL),
    00449   isConnecting(false)
    00450 {
    00451 }
    00452
    00454 ClientNetworkContentSocketHandler::~ClientNetworkContentSocketHandler()
    00455 {
    00456   delete this->curInfo;
    00457   if (this->curFile != NULL) fclose(this->curFile);
    00458
    00459   for (ContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) delete *iter;
    00460 }
    00461
    00462 class NetworkContentConnecter : TCPConnecter {
    00463 public:
    00464   NetworkContentConnecter(const NetworkAddress &address) : TCPConnecter(address) {}
    00465
    00466   virtual void OnFailure()
    00467   {
    00468     _network_content_client.isConnecting = false;
    00469     _network_content_client.OnConnect(false);
    00470   }
    00471
    00472   virtual void OnConnect(SOCKET s)
    00473   {
    00474     assert(_network_content_client.sock == INVALID_SOCKET);
    00475     _network_content_client.isConnecting = false;
    00476     _network_content_client.sock = s;
    00477     _network_content_client.Reopen();
    00478     _network_content_client.OnConnect(true);
    00479   }
    00480 };
    00481
    00485 void ClientNetworkContentSocketHandler::Connect()
    00486 {
    00487   this->lastActivity = _realtime_tick;
    00488
    00489   if (this->sock != INVALID_SOCKET || this->isConnecting) return;
    00490   this->isConnecting = true;
    00491   new NetworkContentConnecter(NetworkAddress(NETWORK_CONTENT_SERVER_HOST, NETWORK_CONTENT_SERVER_PORT, AF_UNSPEC));
    00492 }
    00493
    00497 void ClientNetworkContentSocketHandler::Close()
    00498 {
    00499   if (this->sock == INVALID_SOCKET) return;
    00500   NetworkContentSocketHandler::Close();
    00501
    00502   this->OnDisconnect();
    00503 }
    00504
    00509 void ClientNetworkContentSocketHandler::SendReceive()
    00510 {
    00511   if (this->sock == INVALID_SOCKET || this->isConnecting) return;
    00512
    00513   if (this->lastActivity + IDLE_TIMEOUT < _realtime_tick) {
    00514     this->Close();
    00515     return;
    00516   }
    00517
    00518   fd_set read_fd, write_fd;
    00519   struct timeval tv;
    00520
    00521   FD_ZERO(&read_fd);
    00522   FD_ZERO(&write_fd);
    00523
    00524   FD_SET(this->sock, &read_fd);
    00525   FD_SET(this->sock, &write_fd);
    00526
    00527   tv.tv_sec = tv.tv_usec = 0; // don't block at all.
    00528 #if !defined(__MORPHOS__) && !defined(__AMIGA__)
    00529   select(FD_SETSIZE, &read_fd, &write_fd, NULL, &tv);
    00530 #else
    00531   WaitSelect(FD_SETSIZE, &read_fd, &write_fd, NULL, &tv, NULL);
    00532 #endif
    00533   if (FD_ISSET(this->sock, &read_fd)) {
    00534     this->Recv_Packets();
    00535     this->lastActivity = _realtime_tick;
    00536   }
    00537
    00538   this->writable = !!FD_ISSET(this->sock, &write_fd);
    00539   this->Send_Packets();
    00540 }
    00541
    00546 void ClientNetworkContentSocketHandler::DownloadContentInfo(ContentID cid)
    00547 {
    00548   /* When we tried to download it already, don't try again */
    00549   if (this->requested.Contains(cid)) return;
    00550
    00551   *this->requested.Append() = cid;
    00552   assert(this->requested.Contains(cid));
    00553   this->RequestContentList(1, &cid);
    00554 }
    00555
    00561 ContentInfo *ClientNetworkContentSocketHandler::GetContent(ContentID cid)
    00562 {
    00563   for (ContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) {
    00564     ContentInfo *ci = *iter;
    00565     if (ci->id == cid) return ci;
    00566   }
    00567   return NULL;
    00568 }
    00569
    00570
    00575 void ClientNetworkContentSocketHandler::Select(ContentID cid)
    00576 {
    00577   ContentInfo *ci = this->GetContent(cid);
    00578   if (ci == NULL || ci->state != ContentInfo::UNSELECTED) return;
    00579
    00580   ci->state = ContentInfo::SELECTED;
    00581   this->CheckDependencyState(ci);
    00582 }
    00583
    00588 void ClientNetworkContentSocketHandler::Unselect(ContentID cid)
    00589 {
    00590   ContentInfo *ci = this->GetContent(cid);
    00591   if (ci == NULL || !ci->IsSelected()) return;
    00592
    00593   ci->state = ContentInfo::UNSELECTED;
    00594   this->CheckDependencyState(ci);
    00595 }
    00596
    00598 void ClientNetworkContentSocketHandler::SelectAll()
    00599 {
    00600   for (ContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) {
    00601     ContentInfo *ci = *iter;
    00602     if (ci->state == ContentInfo::UNSELECTED) {
    00603       ci->state = ContentInfo::SELECTED;
    00604       this->CheckDependencyState(ci);
    00605     }
    00606   }
    00607 }
    00608
    00610 void ClientNetworkContentSocketHandler::SelectUpgrade()
    00611 {
    00612   for (ContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) {
    00613     ContentInfo *ci = *iter;
    00614     if (ci->state == ContentInfo::UNSELECTED && ci->upgrade) {
    00615       ci->state = ContentInfo::SELECTED;
    00616       this->CheckDependencyState(ci);
    00617     }
    00618   }
    00619 }
    00620
    00622 void ClientNetworkContentSocketHandler::UnselectAll()
    00623 {
    00624   for (ContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) {
    00625     ContentInfo *ci = *iter;
    00626     if (ci->IsSelected() && ci->state != ContentInfo::ALREADY_HERE) ci->state = ContentInfo::UNSELECTED;
    00627   }
    00628 }
    00629
    00631 void ClientNetworkContentSocketHandler::ToggleSelectedState(const ContentInfo *ci)
    00632 {
    00633   switch (ci->state) {
    00634     case ContentInfo::SELECTED:
    00635     case ContentInfo::AUTOSELECTED:
    00636       this->Unselect(ci->id);
    00637       break;
    00638
    00639     case ContentInfo::UNSELECTED:
    00640       this->Select(ci->id);
    00641       break;
    00642
    00643     default:
    00644       break;
    00645   }
    00646 }
    00647
    00653 void ClientNetworkContentSocketHandler::ReverseLookupDependency(ConstContentVector &parents, const ContentInfo *child) const
    00654 {
    00655   for (ConstContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) {
    00656     const ContentInfo *ci = *iter;
    00657     if (ci == child) continue;
    00658
    00659     for (uint i = 0; i < ci->dependency_count; i++) {
    00660       if (ci->dependencies[i] == child->id) {
    00661         *parents.Append() = ci;
    00662         break;
    00663       }
    00664     }
    00665   }
    00666 }
    00667
    00673 void ClientNetworkContentSocketHandler::ReverseLookupTreeDependency(ConstContentVector &tree, const ContentInfo *child) const
    00674 {
    00675   *tree.Append() = child;
    00676
    00677   /* First find all direct parents */
    00678   for (ConstContentIterator iter = tree.Begin(); iter != tree.End(); iter++) {
    00679     ConstContentVector parents;
    00680     this->ReverseLookupDependency(parents, *iter);
    00681
    00682     for (ConstContentIterator piter = parents.Begin(); piter != parents.End(); piter++) {
    00683       tree.Include(*piter);
    00684     }
    00685   }
    00686 }
    00687
    00692 void ClientNetworkContentSocketHandler::CheckDependencyState(ContentInfo *ci)
    00693 {
    00694   if (ci->IsSelected() || ci->state == ContentInfo::ALREADY_HERE) {
    00695     /* Selection is easy; just walk all children and set the
    00696      * autoselected state. That way we can see what we automatically
    00697      * selected and thus can unselect when a dependency is removed. */
    00698     for (uint i = 0; i < ci->dependency_count; i++) {
    00699       ContentInfo *c = this->GetContent(ci->dependencies[i]);
    00700       if (c == NULL) {
    00701         this->DownloadContentInfo(ci->dependencies[i]);
    00702       } else if (c->state == ContentInfo::UNSELECTED) {
    00703         c->state = ContentInfo::AUTOSELECTED;
    00704         this->CheckDependencyState(c);
    00705       }
    00706     }
    00707     return;
    00708   }
    00709
    00710   if (ci->state != ContentInfo::UNSELECTED) return;
    00711
    00712   /* For unselection we need to find the parents of us. We need to
    00713    * unselect them. After that we unselect all children that we
    00714    * depend on and are not used as dependency for us, but only when
    00715    * we automatically selected them. */
    00716   ConstContentVector parents;
    00717   this->ReverseLookupDependency(parents, ci);
    00718   for (ConstContentIterator iter = parents.Begin(); iter != parents.End(); iter++) {
    00719     const ContentInfo *c = *iter;
    00720     if (!c->IsSelected()) continue;
    00721
    00722     this->Unselect(c->id);
    00723   }
    00724
    00725   for (uint i = 0; i < ci->dependency_count; i++) {
    00726     const ContentInfo *c = this->GetContent(ci->dependencies[i]);
    00727     if (c == NULL) {
    00728       DownloadContentInfo(ci->dependencies[i]);
    00729       continue;
    00730     }
    00731     if (c->state != ContentInfo::AUTOSELECTED) continue;
    00732
    00733     /* Only unselect when WE are the only parent. */
    00734     parents.Clear();
    00735     this->ReverseLookupDependency(parents, c);
    00736
    00737     /* First check whether anything depends on us */
    00738     int sel_count = 0;
    00739     bool force_selection = false;
    00740     for (ConstContentIterator iter = parents.Begin(); iter != parents.End(); iter++) {
    00741       if ((*iter)->IsSelected()) sel_count++;
    00742       if ((*iter)->state == ContentInfo::SELECTED) force_selection = true;
    00743     }
    00744     if (sel_count == 0) {
    00745       /* Nothing depends on us */
    00746       this->Unselect(c->id);
    00747       continue;
    00748     }
    00749     /* Something manually selected depends directly on us */
    00750     if (force_selection) continue;
    00751
    00752     /* "Flood" search to find all items in the dependency graph*/
    00753     parents.Clear();
    00754     this->ReverseLookupTreeDependency(parents, c);
    00755
    00756     /* Is there anything that is "force" selected?, if so... we're done. */
    00757     for (ConstContentIterator iter = parents.Begin(); iter != parents.End(); iter++) {
    00758       if ((*iter)->state != ContentInfo::SELECTED) continue;
    00759
    00760       force_selection = true;
    00761       break;
    00762     }
    00763
    00764     /* So something depended directly on us */
    00765     if (force_selection) continue;
    00766
    00767     /* Nothing depends on us, mark the whole graph as unselected.
    00768      * After that's done run over them once again to test their children
    00769      * to unselect. Don't do it immediatelly because it'll do exactly what
    00770      * we're doing now. */
    00771     for (ConstContentIterator iter = parents.Begin(); iter != parents.End(); iter++) {
    00772       const ContentInfo *c = *iter;
    00773       if (c->state == ContentInfo::AUTOSELECTED) this->Unselect(c->id);
    00774     }
    00775     for (ConstContentIterator iter = parents.Begin(); iter != parents.End(); iter++) {
    00776       this->CheckDependencyState(this->GetContent((*iter)->id));
    00777     }
    00778   }
    00779 }
    00780
    00781 void ClientNetworkContentSocketHandler::Clear()
    00782 {
    00783   for (ContentIterator iter = this->infos.Begin(); iter != this->infos.End(); iter++) delete *iter;
    00784
    00785   this->infos.Clear();
    00786   this->requested.Clear();
    00787 }
    00788
    00789 /*** CALLBACK ***/
    00790
    00791 void ClientNetworkContentSocketHandler::OnConnect(bool success)
    00792 {
    00793   for (ContentCallback **iter = this->callbacks.Begin(); iter != this->callbacks.End(); /* nothing */) {
    00794     ContentCallback *cb = *iter;
    00795     cb->OnConnect(success);
    00796     if (iter != this->callbacks.End() && *iter == cb) iter++;
    00797   }
    00798 }
    00799
    00800 void ClientNetworkContentSocketHandler::OnDisconnect()
    00801 {
    00802   for (ContentCallback **iter = this->callbacks.Begin(); iter != this->callbacks.End(); /* nothing */) {
    00803     ContentCallback *cb = *iter;
    00804     cb->OnDisconnect();
    00805     if (iter != this->callbacks.End() && *iter == cb) iter++;
    00806   }
    00807 }
    00808
    00809 void ClientNetworkContentSocketHandler::OnReceiveContentInfo(const ContentInfo *ci)
    00810 {
    00811   for (ContentCallback **iter = this->callbacks.Begin(); iter != this->callbacks.End(); /* nothing */) {
    00812     ContentCallback *cb = *iter;
    00813     cb->OnReceiveContentInfo(ci);
    00814     if (iter != this->callbacks.End() && *iter == cb) iter++;
    00815   }
    00816 }
    00817
    00818 void ClientNetworkContentSocketHandler::OnDownloadProgress(const ContentInfo *ci, uint bytes)
    00819 {
    00820   for (ContentCallback **iter = this->callbacks.Begin(); iter != this->callbacks.End(); /* nothing */) {
    00821     ContentCallback *cb = *iter;
    00822     cb->OnDownloadProgress(ci, bytes);
    00823     if (iter != this->callbacks.End() && *iter == cb) iter++;
    00824   }
    00825 }
    00826
    00827 void ClientNetworkContentSocketHandler::OnDownloadComplete(ContentID cid)
    00828 {
    00829   ContentInfo *ci = this->GetContent(cid);
    00830   if (ci != NULL) {
    00831     ci->state = ContentInfo::ALREADY_HERE;
    00832   }
    00833
    00834   for (ContentCallback **iter = this->callbacks.Begin(); iter != this->callbacks.End(); /* nothing */) {
    00835     ContentCallback *cb = *iter;
    00836     cb->OnDownloadComplete(cid);
    00837     if (iter != this->callbacks.End() && *iter == cb) iter++;
    00838   }
    00839 }
    00840
    00841 #endif /* ENABLE_NETWORK */