1 /*
2  * This file is part of OpenTTD.
3  * 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.
4  * 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.
5  * 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/>.
6  */
7 
8 /**
9  * @file tcp_content.cpp Basic functions to receive and send Content packets.
10  */
11 
12 #include "../../stdafx.h"
13 #include "../../textfile_gui.h"
14 #include "../../newgrf_config.h"
15 #include "../../base_media_base.h"
16 #include "../../ai/ai.hpp"
17 #include "../../game/game.hpp"
18 #include "../../fios.h"
19 #include "tcp_content.h"
20 
21 #include "../../safeguards.h"
22 
23 /**
24  * Is the state either selected or autoselected?
25  * @return true iff that's the case
26  */
IsSelected() const27 bool ContentInfo::IsSelected() const
28 {
29 	switch (this->state) {
30 		case ContentInfo::SELECTED:
31 		case ContentInfo::AUTOSELECTED:
32 		case ContentInfo::ALREADY_HERE:
33 			return true;
34 
35 		default:
36 			return false;
37 	}
38 }
39 
40 /**
41  * Is the information from this content info valid?
42  * @return true iff it's valid
43  */
IsValid() const44 bool ContentInfo::IsValid() const
45 {
46 	return this->state < ContentInfo::INVALID && this->type >= CONTENT_TYPE_BEGIN && this->type < CONTENT_TYPE_END;
47 }
48 
49 /**
50  * Search a textfile file next to this file in the content list.
51  * @param type The type of the textfile to search for.
52  * @return The filename for the textfile, \c nullptr otherwise.
53  */
GetTextfile(TextfileType type) const54 const char *ContentInfo::GetTextfile(TextfileType type) const
55 {
56 	if (this->state == INVALID) return nullptr;
57 	const char *tmp;
58 	switch (this->type) {
59 		default: NOT_REACHED();
60 		case CONTENT_TYPE_AI:
61 			tmp = AI::GetScannerInfo()->FindMainScript(this, true);
62 			break;
63 		case CONTENT_TYPE_AI_LIBRARY:
64 			tmp = AI::GetScannerLibrary()->FindMainScript(this, true);
65 			break;
66 		case CONTENT_TYPE_GAME:
67 			tmp = Game::GetScannerInfo()->FindMainScript(this, true);
68 			break;
69 		case CONTENT_TYPE_GAME_LIBRARY:
70 			tmp = Game::GetScannerLibrary()->FindMainScript(this, true);
71 			break;
72 		case CONTENT_TYPE_NEWGRF: {
73 			const GRFConfig *gc = FindGRFConfig(BSWAP32(this->unique_id), FGCM_EXACT, this->md5sum);
74 			tmp = gc != nullptr ? gc->filename : nullptr;
75 			break;
76 		}
77 		case CONTENT_TYPE_BASE_GRAPHICS:
78 			tmp = TryGetBaseSetFile(this, true, BaseGraphics::GetAvailableSets());
79 			break;
80 		case CONTENT_TYPE_BASE_SOUNDS:
81 			tmp = TryGetBaseSetFile(this, true, BaseSounds::GetAvailableSets());
82 			break;
83 		case CONTENT_TYPE_BASE_MUSIC:
84 			tmp = TryGetBaseSetFile(this, true, BaseMusic::GetAvailableSets());
85 			break;
86 		case CONTENT_TYPE_SCENARIO:
87 		case CONTENT_TYPE_HEIGHTMAP:
88 			extern const char *FindScenario(const ContentInfo *ci, bool md5sum);
89 			tmp = FindScenario(this, true);
90 			break;
91 	}
92 	if (tmp == nullptr) return nullptr;
93 	return ::GetTextfile(type, GetContentInfoSubDir(this->type), tmp);
94 }
95 
96 /**
97  * Handle the given packet, i.e. pass it to the right
98  * parser receive command.
99  * @param p the packet to handle
100  * @return true if we should immediately handle further packets, false otherwise
101  */
HandlePacket(Packet * p)102 bool NetworkContentSocketHandler::HandlePacket(Packet *p)
103 {
104 	PacketContentType type = (PacketContentType)p->Recv_uint8();
105 
106 	switch (this->HasClientQuit() ? PACKET_CONTENT_END : type) {
107 		case PACKET_CONTENT_CLIENT_INFO_LIST:      return this->Receive_CLIENT_INFO_LIST(p);
108 		case PACKET_CONTENT_CLIENT_INFO_ID:        return this->Receive_CLIENT_INFO_ID(p);
109 		case PACKET_CONTENT_CLIENT_INFO_EXTID:     return this->Receive_CLIENT_INFO_EXTID(p);
110 		case PACKET_CONTENT_CLIENT_INFO_EXTID_MD5: return this->Receive_CLIENT_INFO_EXTID_MD5(p);
111 		case PACKET_CONTENT_SERVER_INFO:           return this->Receive_SERVER_INFO(p);
112 		case PACKET_CONTENT_CLIENT_CONTENT:        return this->Receive_CLIENT_CONTENT(p);
113 		case PACKET_CONTENT_SERVER_CONTENT:        return this->Receive_SERVER_CONTENT(p);
114 
115 		default:
116 			if (this->HasClientQuit()) {
117 				Debug(net, 0, "[tcp/content] Received invalid packet type {}", type);
118 			} else {
119 				Debug(net, 0, "[tcp/content] Received illegal packet");
120 			}
121 			return false;
122 	}
123 }
124 
125 /**
126  * Receive a packet at TCP level
127  * @return Whether at least one packet was received.
128  */
ReceivePackets()129 bool NetworkContentSocketHandler::ReceivePackets()
130 {
131 	/*
132 	 * We read only a few of the packets. This as receiving packets can be expensive
133 	 * due to the re-resolving of the parent/child relations and checking the toggle
134 	 * state of all bits. We cannot do this all in one go, as we want to show the
135 	 * user what we already received. Otherwise, it can take very long before any
136 	 * progress is shown to the end user that something has been received.
137 	 * It is also the case that we request extra content from the content server in
138 	 * case there is an unknown (in the content list) piece of content. These will
139 	 * come in after the main lists have been requested. As a result, we won't be
140 	 * getting everything reliably in one batch. Thus, we need to make subsequent
141 	 * updates in that case as well.
142 	 *
143 	 * As a result, we simple handle an arbitrary number of packets in one cycle,
144 	 * and let the rest be handled in subsequent cycles. These are ran, almost,
145 	 * immediately after this cycle so in speed it does not matter much, except
146 	 * that the user inferface will appear better responding.
147 	 *
148 	 * What arbitrary number to choose is the ultimate question though.
149 	 */
150 	Packet *p;
151 	static const int MAX_PACKETS_TO_RECEIVE = 42;
152 	int i = MAX_PACKETS_TO_RECEIVE;
153 	while (--i != 0 && (p = this->ReceivePacket()) != nullptr) {
154 		bool cont = this->HandlePacket(p);
155 		delete p;
156 		if (!cont) return true;
157 	}
158 
159 	return i != MAX_PACKETS_TO_RECEIVE - 1;
160 }
161 
162 
163 /**
164  * Helper for logging receiving invalid packets.
165  * @param type The received packet type.
166  * @return Always false, as it's an error.
167  */
ReceiveInvalidPacket(PacketContentType type)168 bool NetworkContentSocketHandler::ReceiveInvalidPacket(PacketContentType type)
169 {
170 	Debug(net, 0, "[tcp/content] Received illegal packet type {}", type);
171 	return false;
172 }
173 
Receive_CLIENT_INFO_LIST(Packet * p)174 bool NetworkContentSocketHandler::Receive_CLIENT_INFO_LIST(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CONTENT_CLIENT_INFO_LIST); }
Receive_CLIENT_INFO_ID(Packet * p)175 bool NetworkContentSocketHandler::Receive_CLIENT_INFO_ID(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CONTENT_CLIENT_INFO_ID); }
Receive_CLIENT_INFO_EXTID(Packet * p)176 bool NetworkContentSocketHandler::Receive_CLIENT_INFO_EXTID(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CONTENT_CLIENT_INFO_EXTID); }
Receive_CLIENT_INFO_EXTID_MD5(Packet * p)177 bool NetworkContentSocketHandler::Receive_CLIENT_INFO_EXTID_MD5(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CONTENT_CLIENT_INFO_EXTID_MD5); }
Receive_SERVER_INFO(Packet * p)178 bool NetworkContentSocketHandler::Receive_SERVER_INFO(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CONTENT_SERVER_INFO); }
Receive_CLIENT_CONTENT(Packet * p)179 bool NetworkContentSocketHandler::Receive_CLIENT_CONTENT(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CONTENT_CLIENT_CONTENT); }
Receive_SERVER_CONTENT(Packet * p)180 bool NetworkContentSocketHandler::Receive_SERVER_CONTENT(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CONTENT_SERVER_CONTENT); }
181 
182 /**
183  * Helper to get the subdirectory a #ContentInfo is located in.
184  * @param type The type of content.
185  * @return The subdirectory the content is located in.
186  */
GetContentInfoSubDir(ContentType type)187 Subdirectory GetContentInfoSubDir(ContentType type)
188 {
189 	switch (type) {
190 		default: return NO_DIRECTORY;
191 		case CONTENT_TYPE_AI:           return AI_DIR;
192 		case CONTENT_TYPE_AI_LIBRARY:   return AI_LIBRARY_DIR;
193 		case CONTENT_TYPE_GAME:         return GAME_DIR;
194 		case CONTENT_TYPE_GAME_LIBRARY: return GAME_LIBRARY_DIR;
195 		case CONTENT_TYPE_NEWGRF:       return NEWGRF_DIR;
196 
197 		case CONTENT_TYPE_BASE_GRAPHICS:
198 		case CONTENT_TYPE_BASE_SOUNDS:
199 		case CONTENT_TYPE_BASE_MUSIC:
200 			return BASESET_DIR;
201 
202 		case CONTENT_TYPE_SCENARIO:     return SCENARIO_DIR;
203 		case CONTENT_TYPE_HEIGHTMAP:    return HEIGHTMAP_DIR;
204 	}
205 }
206