1 /* ResidualVM - A 3D game interpreter
2  *
3  * ResidualVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the AUTHORS
5  * file distributed with this source distribution.
6  *
7  * Additional copyright for this file:
8  * Copyright (C) 1999-2000 Revolution Software Ltd.
9  * This code is based on source code created by Revolution Software,
10  * used with permission.
11  *
12  * This program is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU General Public License
14  * as published by the Free Software Foundation; either version 2
15  * of the License, or (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
25  *
26  */
27 
28 #include "engines/icb/common/px_string.h"
29 #include "engines/icb/res_man_pc.h"
30 #include "engines/icb/debug_pc.h"
31 #include "engines/icb/global_objects.h"
32 #include "engines/icb/cluster_manager_pc.h"
33 #include "engines/icb/options_manager_pc.h"
34 #include "engines/icb/movie_pc.h"
35 
36 #include "common/textconsole.h"
37 #include "common/file.h"
38 
39 namespace ICB {
40 
41 // Global ClusterManger instance
42 ClusterManager *g_theClusterManager;
43 
44 // Function prototypes
45 void RecursivelyDeleteDirectory(const char *path);
46 void ValidateDirectoryToDelete(const char *path);
47 uint32 GetFileSize(const char *path);
48 void MakeDirectoryTree(MISSION_ID mission);
49 
50 // Number of bytes to read from CD and write to hard disk per cycle
51 #define CHUNKSIZE 25600 // ((300 * 1024) / 12) based on 12fps for 2xCD drive
52 
53 // This is the number of attempts we check the cd drive on the prompt screen
54 #define CD_SEARCH_DELAY 1000
55 
56 // Private for the title background movie handling
57 MovieManager *g_while_u_wait_SequenceManager;
58 
59 // Colours used by progress display
60 uint32 g_progressColourMap[7] = {
61 	0x3C3C3C, // OFF  60  60  60
62 	0xFEFEFE, //  |   254 254 254
63 	0xCAD5E4, //  |   202 213 228
64 	0xA6B8D4, //  |   166 184 212
65 	0x89A1C7, //  |   137 161 199
66 	0x587AB0, //  V   88  122 176
67 	0x2E579C  // ON   46  87  156
68 };
69 
70 // Controls the decay on the progress bit colouring
71 #define PROGRESS_BIT_DELAY 6
72 
ClusterManager()73 ClusterManager::ClusterManager() {
74 	memset(m_cdroot1, 0, 1024);
75 	memset(m_cdroot2, 0, 1024);
76 	m_multipleCDDrives = FALSE8;
77 	m_activeCDDrive = 1;
78 	memset(m_missionDir, 0, 8);
79 	m_bytesFreeOnInstalledDrive = 0;
80 	m_minimumInstall = FALSE8;
81 
82 	memset(m_theList, 0, MAX_BYTESIZE_OF_A_FILELIST);
83 	m_filelistTotalBytes = 0;
84 	m_filelistCursor = -1;
85 	m_filelistSize = 0;
86 
87 	m_src_fp = NULL;
88 	m_dst_fp = NULL;
89 
90 	m_currentFileSize = 0;
91 	m_chunkCounter = 0;
92 	m_bytesDone = 0;
93 
94 	m_movieMemoryPointer = NULL;
95 
96 	m_installDone = FALSE8;
97 
98 	memset(m_progressBits, 0, sizeof(PROGRESS_BIT) * NUMBER_OF_PROGRESS_BITS);
99 	m_bitsDone = 0;
100 	m_frameCounter = 0;
101 	m_currentLanguage = T_ENGLISH;
102 }
103 
~ClusterManager()104 ClusterManager::~ClusterManager() {}
105 
Initialise()106 void ClusterManager::Initialise() {
107 	// First we need to discover what install method has been employed.
108 	MinimumInstallCheck();
109 
110 	// Obtain drive information (CD path(s) and free space)
111 	InterrogateDrives();
112 
113 	// Starting with an empty mission directory covers against the game
114 	// crashing out or data being modified between executes.
115 	CleanHardDisk();
116 
117 	// Require a disc on startup
118 	CheckAnyDiscInserted();
119 
120 	// Line number 7398
121 	const char *testline = g_theOptionsManager->GetTextFromReference(HashString("opt_missingdisc"));
122 
123 	if (strcmp(testline, "Please insert disc %d") == 0)
124 		m_currentLanguage = T_ENGLISH;
125 	else if (strcmp(testline, "Veuillez ins\xE9rer le disque %d") == 0)
126 		m_currentLanguage = T_FRENCH;
127 	else if (strcmp(testline, "Inserisci il disco %d") == 0)
128 		m_currentLanguage = T_ITALIAN;
129 	else if (strcmp(testline, "Bitte CD %d einlegen") == 0)
130 		m_currentLanguage = T_GERMAN;
131 	else if (strcmp(testline, "Por favor, inserta el disco %d") == 0)
132 		m_currentLanguage = T_SPANISH;
133 	else if (strcmp(testline, "\xC2\xF1\xF2\xE0\xE2\xFC\xF2\xE5 \xE4\xE8\xF1\xEA %d") == 0)
134 		m_currentLanguage = T_RUSSIAN;
135 	else
136 		// Must be polish by default
137 		m_currentLanguage = T_POLISH;
138 }
139 
CheckDiscInserted(MISSION_ID)140 void ClusterManager::CheckDiscInserted(MISSION_ID /*mission*/) {
141 }
142 
CheckDiscInsertedWithCancel(MISSION_ID mission)143 bool8 ClusterManager::CheckDiscInsertedWithCancel(MISSION_ID mission) {
144 	// No user cancel
145 	return FALSE8;
146 }
147 
CheckAnyDiscInserted()148 void ClusterManager::CheckAnyDiscInserted() {
149 }
150 
StartMissionInstall(MISSION_ID)151 bool8 ClusterManager::StartMissionInstall(MISSION_ID /*mission*/) {
152 	return FALSE8;
153 }
154 
InstallMission()155 bool8 ClusterManager::InstallMission() {
156 	// Nothing to do on a full install
157 	if (m_minimumInstall == FALSE8)
158 		return FALSE8;
159 
160 	return FALSE8;
161 }
162 
InterrogateDrives()163 void ClusterManager::InterrogateDrives() {
164 }
165 
CalculateFreeDiskSpace(void)166 void ClusterManager::CalculateFreeDiskSpace(void) {
167 	m_bytesFreeOnInstalledDrive = 256 * 1024 * 1024;
168 }
169 
GetCDRoot(void)170 char *ClusterManager::GetCDRoot(void) {
171 	if (m_activeCDDrive == 1)
172 		return m_cdroot1;
173 	else
174 		return m_cdroot2;
175 }
176 
WhichCD(MISSION_ID mission)177 int32 ClusterManager::WhichCD(MISSION_ID mission) {
178 	// All demos exist on one CD only
179 	int32 demo = g_globalScriptVariables->GetVariable("demo");
180 	if (demo != 0)
181 		return 1;
182 
183 	if (mission >= MISSION1 && mission <= MISSION3)
184 		return 1;
185 	else if (mission >= MISSION4 && mission <= MISSION7)
186 		return 2;
187 	else if (mission >= MISSION8 && mission <= MISSION10)
188 		return 3;
189 	else
190 		Fatal_error("ClusterManager::WhichCD() can't resolve unknown mission parameter");
191 
192 	// Never gonna get here are we
193 	return 0;
194 }
195 
CheckForCD(int32)196 bool8 ClusterManager::CheckForCD(int32 /*number*/) {
197 	strcpy(m_cdroot1, "");
198 	strcpy(m_cdroot2, "");
199 	return TRUE8;
200 }
201 
MinimumInstallCheck()202 void ClusterManager::MinimumInstallCheck() {
203 	m_minimumInstall = FALSE8;
204 }
205 
IsMissionDataInstalled(MISSION_ID & m)206 bool8 ClusterManager::IsMissionDataInstalled(MISSION_ID &m) {
207 	for (uint32 i = 0; i < NUMBER_OF_MISSIONS; i++) {
208 		// Make the mission directories one by one and see if any exist
209 		char h_mission[8];
210 		HashFile(g_mission_names[i], h_mission);
211 
212 		pxString missionDirectory;
213 		missionDirectory.Format("m\\%s\\", h_mission);
214 
215 		if (checkFileExists(missionDirectory)) {
216 			m = (MISSION_ID)i;
217 			return TRUE8;
218 		}
219 	}
220 
221 	// No mission directories on the hard disk
222 	return FALSE8;
223 }
224 
CleanHardDisk()225 void ClusterManager::CleanHardDisk() {
226 	// Can't be letting that happen now can we
227 	if (m_minimumInstall == FALSE8)
228 		return;
229 }
230 
MissingCD(int32)231 void ClusterManager::MissingCD(int32 /*number*/) {
232 }
233 
MissingCDWithCancel(int32)234 bool8 ClusterManager::MissingCDWithCancel(int32 /*number*/) {
235 	return FALSE8;
236 }
237 
LoadFileList(MISSION_ID)238 void ClusterManager::LoadFileList(MISSION_ID /*mission*/) {
239 	if (m_minimumInstall == FALSE8)
240 		return;
241 }
242 
GetFileListEntry()243 char *ClusterManager::GetFileListEntry() {
244 	if (m_filelistCursor == -1)
245 		Fatal_error("Can't retrieve filelist entry without loading a filelist first!");
246 
247 	char *line = NULL;
248 
249 	// End of file check
250 	if (m_filelistCursor < m_filelistSize) {
251 		line = &(m_theList[m_filelistCursor]);
252 
253 		// Move to next line
254 		m_filelistCursor += strlen((const char *)&(m_theList[m_filelistCursor]));
255 
256 		// Skip any terminators to get to the start of the next line
257 		while (m_theList[m_filelistCursor] == 0)
258 			m_filelistCursor++;
259 	}
260 
261 	return line;
262 }
263 
DrawCoverFrame(void)264 bool8 ClusterManager::DrawCoverFrame(void) {
265 	// Draw a frame of the torture movie
266 	g_while_u_wait_SequenceManager->drawFrame(working_buffer_id);
267 
268 	// Have we finished both movie playback and mission install
269 	if (m_installDone) {
270 		// Release bink from playing the movie
271 		g_while_u_wait_SequenceManager->kill();
272 		// Free up resources
273 		delete[] m_movieMemoryPointer;
274 
275 		// Quit only when movie has finished
276 		return FALSE8;
277 	}
278 
279 	DrawProgressBits();
280 
281 	// Update screen manually
282 	surface_manager->Flip();
283 
284 	return TRUE8;
285 }
286 
InitialiseProgressBits()287 void ClusterManager::InitialiseProgressBits() {
288 	// Tweakable
289 	int32 width = 5;
290 	int32 height = 15;
291 	int32 spacing = 2;
292 	int32 initialY = SCREEN_DEPTH - height - 30;
293 
294 	// Calculate entire width so we can centre things
295 	int32 length = (NUMBER_OF_PROGRESS_BITS * (width + spacing)) - spacing;
296 	int32 initialX = (SCREEN_WIDTH / 2) - (length / 2);
297 
298 	for (int32 i = 0; i < NUMBER_OF_PROGRESS_BITS; i++) {
299 		m_progressBits[i].r.left = initialX;
300 		m_progressBits[i].r.top = initialY;
301 		m_progressBits[i].r.right = initialX + width;
302 		m_progressBits[i].r.bottom = initialY + height;
303 
304 		m_progressBits[i].state = 0;
305 
306 		// Now increment
307 		initialX += width + spacing;
308 	}
309 
310 	m_bitsDone = 0;
311 }
312 
UpdateProgressBits()313 void ClusterManager::UpdateProgressBits() {
314 	if (m_frameCounter % PROGRESS_BIT_DELAY == 0) {
315 		// Update the state of all bits
316 		for (int32 i = 0; i < NUMBER_OF_PROGRESS_BITS; i++) {
317 			if (m_progressBits[i].state > 0 && m_progressBits[i].state != 6) {
318 				m_progressBits[i].state = m_progressBits[i].state + 1;
319 			}
320 		}
321 	}
322 
323 	// Bytes per progress bit
324 	float progress_inc = (float)(m_filelistTotalBytes / NUMBER_OF_PROGRESS_BITS);
325 
326 	// The number of bits that should bit switched on
327 	uint32 bitsOn = (int32)((float)m_bytesDone / progress_inc);
328 
329 	// Do we need to switch on a new bit
330 	if (bitsOn > m_bitsDone) {
331 		m_progressBits[m_bitsDone].state = 1;
332 		m_bitsDone++;
333 	}
334 }
335 
DrawProgressBits()336 void ClusterManager::DrawProgressBits() {
337 	for (int32 i = 0; i < NUMBER_OF_PROGRESS_BITS; i++) {
338 		Fill_rect(m_progressBits[i].r.left, m_progressBits[i].r.top, m_progressBits[i].r.right, m_progressBits[i].r.bottom, g_progressColourMap[m_progressBits[i].state]);
339 	}
340 }
341 
Shutdown(void)342 void ClusterManager::Shutdown(void) {
343 }
344 
RecursivelyDeleteDirectory(const char *)345 void RecursivelyDeleteDirectory(const char * /*path*/) {
346 }
347 
ValidateDirectoryToDelete(const char * path)348 void ValidateDirectoryToDelete(const char *path) {
349 	if (strcmp(path, pxVString("m\\FP3YNHA\\")) == 0)
350 		return;
351 	else if (strcmp(path, pxVString("m\\HWYIPVA\\")) == 0)
352 		return;
353 	else if (strcmp(path, pxVString("m\\TPQUB4D\\")) == 0)
354 		return;
355 	else if (strcmp(path, pxVString("m\\RIGABTB\\")) == 0)
356 		return;
357 	else if (strcmp(path, pxVString("m\\GAIYO3A\\")) == 0)
358 		return;
359 	else if (strcmp(path, pxVString("m\\NMUFF0B\\")) == 0)
360 		return;
361 	else if (strcmp(path, pxVString("m\\1QYUOAA\\")) == 0)
362 		return;
363 	else if (strcmp(path, pxVString("m\\TT3WADD\\")) == 0)
364 		return;
365 	else
366 		Fatal_error(pxVString("ValidateDirectoryToDelete() failed on: %s", path));
367 }
368 
GetFileSize(const char * path)369 uint32 GetFileSize(const char *path) {
370 	Common::File file;
371 
372 	if (!file.open(path)) {
373 		return 0;
374 	}
375 
376 	return (uint32)file.size();
377 }
378 
MissionIdToName(MISSION_ID mission)379 const char *MissionIdToName(MISSION_ID mission) {
380 	switch (mission) {
381 	case MISSION1:
382 		return g_m01;
383 	case MISSION2:
384 		return g_m02;
385 	case MISSION3:
386 		return g_m03;
387 	case MISSION4:
388 		return g_m04;
389 	case MISSION5:
390 		return g_m05;
391 	case MISSION7:
392 		return g_m07;
393 	case MISSION8:
394 		return g_m08;
395 	case MISSION9:
396 		return g_m08;
397 	case MISSION10:
398 		return g_m10;
399 	}
400 
401 	Fatal_error("MissionIdToName() should never get here - smack AndyB");
402 	return NULL;
403 }
404 
MakeDirectoryTree(MISSION_ID)405 void MakeDirectoryTree(MISSION_ID /*mission*/) {
406 }
407 
408 } // End of namespace ICB
409