/* ResidualVM - A 3D game interpreter * * ResidualVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the AUTHORS * file distributed with this source distribution. * * Additional copyright for this file: * Copyright (C) 1999-2000 Revolution Software Ltd. * This code is based on source code created by Revolution Software, * used with permission. * * This program 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; either version 2 * of the License, or (at your option) any later version. * * This program 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. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include "engines/icb/common/px_rccommon.h" #include "engines/icb/common/px_string.h" #include "engines/icb/res_man_pc.h" #include "engines/icb/debug_pc.h" #include "engines/icb/global_objects.h" #include "engines/icb/cluster_manager_pc.h" #include "engines/icb/options_manager_pc.h" #include "engines/icb/movie_pc.h" #include "common/textconsole.h" #include "common/file.h" namespace ICB { // Global ClusterManger instance ClusterManager *g_theClusterManager; // Function prototypes void RecursivelyDeleteDirectory(const char *path); void ValidateDirectoryToDelete(const char *path); uint32 GetFileSize(const char *path); void MakeDirectoryTree(MISSION_ID mission); // Number of bytes to read from CD and write to hard disk per cycle #define CHUNKSIZE 25600 // ((300 * 1024) / 12) based on 12fps for 2xCD drive // This is the number of attempts we check the cd drive on the prompt screen #define CD_SEARCH_DELAY 1000 // Private for the title background movie handling MovieManager *g_while_u_wait_SequenceManager; // Colours used by progress display uint32 g_progressColourMap[7] = { 0x3C3C3C, // OFF 60 60 60 0xFEFEFE, // | 254 254 254 0xCAD5E4, // | 202 213 228 0xA6B8D4, // | 166 184 212 0x89A1C7, // | 137 161 199 0x587AB0, // V 88 122 176 0x2E579C // ON 46 87 156 }; // Controls the decay on the progress bit colouring #define PROGRESS_BIT_DELAY 6 ClusterManager::ClusterManager() { memset(m_cdroot1, 0, 1024); memset(m_cdroot2, 0, 1024); m_multipleCDDrives = FALSE8; m_activeCDDrive = 1; memset(m_missionDir, 0, 8); m_bytesFreeOnInstalledDrive = 0; m_minimumInstall = FALSE8; memset(m_theList, 0, MAX_BYTESIZE_OF_A_FILELIST); m_filelistTotalBytes = 0; m_filelistCursor = -1; m_filelistSize = 0; m_src_fp = NULL; m_dst_fp = NULL; m_currentFileSize = 0; m_chunkCounter = 0; m_bytesDone = 0; m_movieMemoryPointer = NULL; m_installDone = FALSE8; memset(m_progressBits, 0, NUMBER_OF_PROGRESS_BITS); m_bitsDone = 0; m_frameCounter = 0; m_currentLanguage = T_ENGLISH; } ClusterManager::~ClusterManager() {} void ClusterManager::Initialise() { // First we need to discover what install method has been employed. MinimumInstallCheck(); // Obtain drive information (CD path(s) and free space) InterrogateDrives(); // Starting with an empty mission directory covers against the game // crashing out or data being modified between executes. CleanHardDisk(); // Require a disc on startup CheckAnyDiscInserted(); // Line number 7398 const char *testline = g_theOptionsManager->GetTextFromReference(HashString("opt_missingdisc")); if (strcmp(testline, "Please insert disc %d") == 0) m_currentLanguage = T_ENGLISH; else if (strcmp(testline, "Veuillez ins\xE9rer le disque %d") == 0) m_currentLanguage = T_FRENCH; else if (strcmp(testline, "Inserisci il disco %d") == 0) m_currentLanguage = T_ITALIAN; else if (strcmp(testline, "Bitte CD %d einlegen") == 0) m_currentLanguage = T_GERMAN; else if (strcmp(testline, "Por favor, inserta el disco %d") == 0) m_currentLanguage = T_SPANISH; else if (strcmp(testline, "\xC2\xF1\xF2\xE0\xE2\xFC\xF2\xE5 \xE4\xE8\xF1\xEA %d") == 0) m_currentLanguage = T_RUSSIAN; else // Must be polish by default m_currentLanguage = T_POLISH; } void ClusterManager::CheckDiscInserted(MISSION_ID /*mission*/) { #if 0 // To kill compiler warning mission; #ifdef PC_DEMO return; #endif #ifdef FROM_PC_CD int cd = WhichCD(mission); if (!CheckForCD(cd)) { MissingCD(cd); } #endif #endif } bool8 ClusterManager::CheckDiscInsertedWithCancel(MISSION_ID mission) { #if 0 #ifdef PC_DEMO return TRUE8; #endif #if 0 // was #ifdef FROM_PC_CD int cd = WhichCD(mission); if (!CheckForCD(cd)) { return MissingCDWithCancel(cd); } #endif #endif // No user cancel return FALSE8; } void ClusterManager::CheckAnyDiscInserted() { #if 0 #ifdef PC_DEMO return; #endif if (!CheckForCD(1)) { if (!CheckForCD(2)) { if (!CheckForCD(3)) MissingCD(1); } } #endif } bool8 ClusterManager::StartMissionInstall(MISSION_ID /*mission*/) { return FALSE8; #if 0 // First off ensure we have the correct CD in the drive for this mission CheckDiscInserted(mission); // Nothing more to do on a full install if (m_minimumInstall == FALSE8) return FALSE8; // Check if we already have this mission on the disk MISSION_ID on_disk; if (IsMissionDataInstalled(on_disk)) { if (mission == on_disk) { // Data already there return FALSE8; } } // Set flag to false m_installDone = FALSE8; // Pointer to the mission name char *missionName = mission_names[mission]; // Load the loading movie :) // Full path to the torture movie (from hard disk at the moment) pxString loadingMovie; loadingMovie.Format("gmovies\\loading.bik"); loadingMovie.ConvertPath(); // Load the movie into memory for playback uint32 movieSize = GetFileSize(loadingMovie); if (movieSize == 0) Fatal_error("Couldn't get filesize of loading movie"); // Get storage m_movieMemoryPointer = new char[movieSize]; // Open the movie file and read it straight into memory FILE *movieFp = openDiskFileForBinaryRead(loadingMovie); if (movieFp == NULL) Fatal_error(pxVString("Failed to open movie file: %s for reading", (const char *)loadingMovie)); if (fread(m_movieMemoryPointer, 1, movieSize, movieFp) != movieSize) Fatal_error("ClusterManager::StartMissionInstall() failed to read from file"); // Close the file fclose(movieFp); // Register this pointer with bink if (!g_while_u_wait_SequenceManager.Register(m_movieMemoryPointer, FALSE8, TRUE8, BINKFROMMEMORY)) { Fatal_error("ClusterManager::StartMissionInstall() - Couldn't register loading movie"); } // Reset progress display InitialiseProgressBits(); m_bytesDone = 0; m_frameCounter = 0; // Deletion might take a second or so, so draw initial frame DrawCoverFrame(); // First thing to do is ensure no mission data is on the hard disk CleanHardDisk(); // Load the appropriate filelist to memory for parsing LoadFileList(mission); // As we now know that mission directory is empty check current disk space CalculateFreeDiskSpace(); // Now validate that we have room to put this mission to the hard-disk if (m_filelistTotalBytes > m_bytesFreeOnInstalledDrive) { // Hard to know how to deal with this really Fatal_error("Not enough disk space to install mission. Please make some room and try again."); } // Ensure all required directories exist for this mission MakeDirectoryTree(mission); return TRUE8; #endif } bool8 ClusterManager::InstallMission() { // Nothing to do on a full install if (m_minimumInstall == FALSE8) return FALSE8; // Just to allow compilation on XCode: return FALSE8; #if 0 char *filename; char sourcePath[128]; char destPath[128]; // Do we need to open new files if (m_src_fp == NULL && m_dst_fp == NULL) { // Get next filename from the filelist filename = GetFileListEntry(); // Have we finished if (filename != NULL) { // Construct the new source and destination names sprintf(sourcePath, "m\\%s\\%s", m_missionDir, filename); sprintf(destPath, "m\\%s\\%s", m_missionDir, filename); // Get size m_currentFileSize = GetFileSize(sourcePath); if (m_currentFileSize == 0) Fatal_error(pxVString("Couldn't get size of file: %s", sourcePath)); // New file so reset chunk counter m_chunkCounter = 0; // Open the source file m_src_fp = openDiskFileForBinaryRead(sourcePath); if (m_src_fp == NULL) Fatal_error(pxVString("ClusterManager::InstallMission() couldn't open file: %s for reading", sourcePath)); // Now open the destination file m_dst_fp = openDiskFileForBinaryWrite(destPath); if (m_dst_fp == NULL) Fatal_error(pxVString("ClusterManager::InstallMission() couldn't open file: %s for writing", destPath)); } else { // Huzzah! All done! m_installDone = TRUE8; } } if (m_installDone == FALSE8) { // Default chunk size uint32 chunkSize = CHUNKSIZE; // End of file flag bool8 endOfFile = FALSE8; // Do we need to deal with the dreggs of a file if ((m_chunkCounter + 1) * CHUNKSIZE > m_currentFileSize) { // Calculate size of dog end chunkSize = m_currentFileSize - (m_chunkCounter * CHUNKSIZE); endOfFile = TRUE8; } // Get a chunk sized piece of memory char *data = new char[chunkSize]; if (data == NULL) Fatal_error("ClusterManager::InstallMission() couldn't get memory!"); // Read source file to memory if (fread(data, 1, chunkSize, m_src_fp) != chunkSize) Fatal_error("ClusterManager::InstallMission() failed to read from file"); // Write memory to destination file if (fwrite(data, 1, chunkSize, m_dst_fp) != chunkSize) Fatal_error("ClusterManager::InstallMission() failed to write to file"); // Free up the memory we've used delete [] data; // Done a chunk m_chunkCounter++; m_bytesDone += chunkSize; // End of file check if (endOfFile) { // End of file reached // Close the source file fclose(m_src_fp); m_src_fp = NULL; // Close the destination file fclose(m_dst_fp); m_dst_fp = NULL; } } // Update progress status UpdateProgressBits(); // Increment counter m_frameCounter++; // Update the screen visuals return DrawCoverFrame(); #endif } void ClusterManager::InterrogateDrives() { #if 0 int buffersize = 128; char driveStrings[128]; m_multipleCDDrives = FALSE8; // Fill with terminators memset(driveStrings, 0, buffersize); // This function lists all drives on the current system separated by terminators int len = GetLogicalDriveStrings(buffersize, driveStrings); // A pointer to the string list char *ptr = driveStrings; int cursor = 0; bool8 foundCDdrive = FALSE8; while (cursor < len) { if (GetDriveType(ptr + cursor) == DRIVE_CDROM) { if (foundCDdrive == FALSE) { // Record the drive that's the (first) CD drive strcpy(m_cdroot1, (const char *)(ptr + cursor)); foundCDdrive = TRUE8; } else { // Record the drive that's the (second) CD drive strcpy(m_cdroot2, (const char *)(ptr + cursor)); m_multipleCDDrives = TRUE8; break; } } // Move to next drive sting in the list cursor += strlen((const char *)ptr + cursor) + 1; } // Now see how much space we have on the current (therefore installed) drive CalculateFreeDiskSpace(); #endif } void ClusterManager::CalculateFreeDiskSpace(void) { warning("STUB: ClusterManager::CalculateFreeDiskSpace()"); #if 0 DWORD sectorsPerCluster; DWORD bytesPerSector; DWORD numberOfFreeClusters; DWORD totalNumberOfClusters; if (GetDiskFreeSpace(NULL, §orsPerCluster, &bytesPerSector, &numberOfFreeClusters, &totalNumberOfClusters) == 0) { char error_message[256]; // Figure out why we failed FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&error_message, 0, NULL); Fatal_error(pxVString("ClusterManager::InterrogateDrives() GetLastError(): %s", error_message)); } // For megabytes free, divide this number by (1024*1024) m_bytesFreeOnInstalledDrive = numberOfFreeClusters * sectorsPerCluster * bytesPerSector; #endif m_bytesFreeOnInstalledDrive = 256 * 1024 * 1024; } char *ClusterManager::GetCDRoot(void) { if (m_activeCDDrive == 1) return m_cdroot1; else return m_cdroot2; } int ClusterManager::WhichCD(MISSION_ID mission) { // All demos exist on one CD only int demo = g_globalScriptVariables.GetVariable("demo"); if (demo != 0) return 1; if (mission >= MISSION1 && mission <= MISSION3) return 1; else if (mission >= MISSION4 && mission <= MISSION7) return 2; else if (mission >= MISSION8 && mission <= MISSION10) return 3; else Fatal_error("ClusterManager::WhichCD() can't resolve unknown mission parameter"); // Never gonna get here are we return 0; } bool8 ClusterManager::CheckForCD(int /*number*/) { #if 1 // was #ifdef FROM_PC_CD strcpy(m_cdroot1, ""); strcpy(m_cdroot2, ""); return TRUE8; #else // FROM_PC_CD return TRUE8; #endif #if 0 char volumeName[128]; char volumeName2[128]; DWORD volumeNameLength = 128; DWORD unused = 0; // This is to prevent against windows getting arsey when there's no CD in the CD // drive; which, of course, is what we're trying to handle within this code. UINT oldErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS); // Get the volume name - don't care about anything else GetVolumeInformation(m_cdroot1, volumeName, volumeNameLength, NULL, NULL, NULL, NULL, unused); if (m_multipleCDDrives) GetVolumeInformation(m_cdroot2, volumeName2, volumeNameLength, NULL, NULL, NULL, NULL, unused); // Return this to it's previous state now that we've finished SetErrorMode(oldErrorMode); char *seekingVolume = NULL; if (number == 1) seekingVolume = "ICB_CD1"; else if (number == 2) seekingVolume = "ICB_CD2"; else if (number == 3) seekingVolume = "ICB_CD3"; else Fatal_error(pxVString("ClusterManager::CheckForCD(# %d) unknown CD number to find!", number)); // Is the correctly named CD in the drive if (strcmp(volumeName, seekingVolume) == 0) { m_activeCDDrive = 1; return TRUE8; } // Multiple drive support if (m_multipleCDDrives) { if (strcmp(volumeName2, seekingVolume) == 0) { m_activeCDDrive = 2; return TRUE8; } } return FALSE8; #endif } void ClusterManager::MinimumInstallCheck() { #if 0 // Look for an empty file that the installer placed next to the engine if (checkFileExists("minimum", 0)) m_minimumInstall = TRUE8; else #endif m_minimumInstall = FALSE8; } bool8 ClusterManager::IsMissionDataInstalled(MISSION_ID &m) { for (uint32 i = 0; i < NUMBER_OF_MISSIONS; i++) { // Make the mission directories one by one and see if any exist char h_mission[8]; HashFile(g_mission_names[i], h_mission); pxString missionDirectory; missionDirectory.Format("m\\%s\\", h_mission); if (checkFileExists(missionDirectory)) { m = (MISSION_ID)i; return TRUE8; } } // No mission directories on the hard disk return FALSE8; } void ClusterManager::CleanHardDisk() { // Can't be letting that happen now can we if (m_minimumInstall == FALSE8) return; #if 0 // This needs to be a recursive delete under the clustered mission directory // Ideally this would only need to remove a single mission at a time // but to be safe I think it should try to remove everything under missions // For all missions for (uint32 i = 0; i < NUMBER_OF_MISSIONS; i++) { char h_mission[8]; HashFile(mission_names[i], h_mission); pxString missionDirectory; missionDirectory.Format("m\\%s\\", h_mission); // First determine if this mission is on the hard disk if (!checkFileExists(missionDirectory)) // amode = 0 continue; // Validation function (don't want to be deleting shit without being 100%) ValidateDirectoryToDelete(missionDirectory); // Remove everything under the mission directory RecursivelyDeleteDirectory(missionDirectory); // Finally delete the mission directory itself (only possible if empty) // Need to make sure that it's not read-only first _chmod(missionDirectory, _S_IWRITE); // Delete directory if (rmdir(missionDirectory) == -1) { int lastChar = missionDirectory.GetLen() - 1; // Need to chop of slash character if (missionDirectory[lastChar] == '\\') { missionDirectory[lastChar] = '\0'; // Try again if (rmdir(missionDirectory) == -1) { // Couldn't delete this! Fatal_error(pxVString("Couldn't delete directory: %s", (const char *)missionDirectory)); } } else { // Couldn't delete this! Fatal_error(pxVString("Couldn't delete directory: %s", (const char *)missionDirectory)); } } } #endif } void ClusterManager::MissingCD(int /*number*/) { #if 0 uint32 halfScreen = SCREEN_WIDTH / 2; uint32 temp; char msg[128]; uint8 *ad; uint32 pitch; // This function needs to display a pleasant message and loop internally until we either // detect the correct cd in the drive or the user cancels const char *message = g_theOptionsManager->GetTextFromReference(HashString("opt_missingdisc")); char *ok = (char *)g_theOptionsManager->GetTextFromReference(HashString("opt_continue")); char *quit = (char *)g_theOptionsManager->GetTextFromReference(HashString("opt_quit")); // Blank the screen surface_manager->Clear_surface(working_buffer_id); sprintf(msg, message, number); bool8 okSelected = TRUE8; bool8 checking = FALSE8; bool8 gotCD = FALSE8; while (gotCD == FALSE8) { // Important this bit Poll_direct_input(); // Deal with user input first if (Read_DI_once_keys(Common::KEYCODE_RETURN) || Read_DI_once_keys(Common::KEYCODE_SPACE)) { if (okSelected) { checking = TRUE8; } else { // User wishes to quit Common::Event event; event.type = Common::EVENT_QUIT; g_system->getEventManager()->pushEvent(event); return; } } if (Read_DI_once_keys(Common::KEYCODE_LEFT) || Read_DI_once_keys(Common::KEYCODE_RIGHT)) { // Toggle selection if (okSelected) okSelected = FALSE8; else okSelected = TRUE8; } // This block looks for the cd over a short loop if (checking) { // Alter the screen display to show we are checking ad = surface_manager->Lock_surface(working_buffer_id); pitch = surface_manager->Get_pitch(working_buffer_id); // Display message g_theOptionsManager->DisplayText(ad, pitch, msg, 0, 90, PALEFONT, TRUE8); // Display choices temp = g_theOptionsManager->CalculateStringWidth(ok); g_theOptionsManager->DisplayText(ad, pitch, ok, halfScreen - temp - 20, 130, PALEFONT, FALSE8); g_theOptionsManager->DisplayText(ad, pitch, quit, halfScreen + 20, 130, PALEFONT, FALSE8); surface_manager->Unlock_surface(working_buffer_id); // Update screen manually surface_manager->Flip(); for (uint32 tik = 0; tik < CD_SEARCH_DELAY; tik++) { // Check for the CD again if (CheckForCD(number)) { // Cool, got it // Black out screen manually surface_manager->Clear_surface(working_buffer_id); surface_manager->Flip(); gotCD = TRUE8; break; } } checking = FALSE8; } // we don't use this function so SendMessage is just disabled //SendMessage(windowHandle, WM_PAINT, 0, 0); if (gotCD) break; ad = surface_manager->Lock_surface(working_buffer_id); pitch = surface_manager->Get_pitch(working_buffer_id); // Display message g_theOptionsManager->DisplayText(ad, pitch, msg, 0, 90, NORMALFONT, TRUE8, TRUE8); // Display choices temp = g_theOptionsManager->CalculateStringWidth(ok); g_theOptionsManager->DisplayText(ad, pitch, ok, halfScreen - temp - 20, 130, (bool8)(okSelected == TRUE8) ? SELECTEDFONT : NORMALFONT, FALSE8); g_theOptionsManager->DisplayText(ad, pitch, quit, halfScreen + 20, 130, (bool8)(okSelected == FALSE8) ? SELECTEDFONT : NORMALFONT, FALSE8); surface_manager->Unlock_surface(working_buffer_id); // Update screen manually surface_manager->Flip(); } #endif } bool8 ClusterManager::MissingCDWithCancel(int /*number*/) { #if 0 uint32 halfScreen = SCREEN_WIDTH / 2; uint32 temp; char msg[128]; uint8 *ad; uint32 pitch; // This function needs to display a pleasant message and loop internally until we either // detect the correct cd in the drive or the user cancels const char *message = g_theOptionsManager->GetTextFromReference(HashString("opt_missingdisc")); char *ok = (char *)g_theOptionsManager->GetTextFromReference(HashString("opt_continue")); char *quit = (char *)g_theOptionsManager->GetTextFromReference(HashString("opt_exitgame")); char *cancel = (char *)g_theOptionsManager->GetTextFromReference(HashString("opt_back")); // Blank the screen surface_manager->Clear_surface(working_buffer_id); sprintf(msg, message, number); int selection = 0; bool8 checking = FALSE8; bool8 gotCD = FALSE8; while (gotCD == FALSE8) { // Important this bit Poll_direct_input(); // Deal with user input first if (Read_DI_once_keys(Common::KEYCODE_RETURN) || Read_DI_once_keys(Common::KEYCODE_SPACE)) { if (selection == 0) { checking = TRUE8; } else if (selection == 1) { // User cancelled return TRUE8; } else { // User wishes to quit Common::Event event; event.type = Common::EVENT_QUIT; g_system->getEventManager()->pushEvent(event); return TRUE8; } } if (Read_DI_once_keys(Common::KEYCODE_UP)) { // Toggle selection if (selection == 0) selection = 2; else selection--; } if (Read_DI_once_keys(Common::KEYCODE_DOWN)) { // Toggle selection if (selection == 2) selection = 0; else selection++; } // This block looks for the cd over a short loop if (checking) { // Alter the screen display to show we are checking ad = surface_manager->Lock_surface(working_buffer_id); pitch = surface_manager->Get_pitch(working_buffer_id); // Display message g_theOptionsManager->DisplayText(ad, pitch, msg, 0, 90, PALEFONT, TRUE8); // Display choices temp = g_theOptionsManager->CalculateStringWidth(ok); g_theOptionsManager->DisplayText(ad, pitch, ok, halfScreen - (temp / 2), 130, PALEFONT, FALSE8); temp = g_theOptionsManager->CalculateStringWidth(cancel); g_theOptionsManager->DisplayText(ad, pitch, cancel, halfScreen - (temp / 2), 160, PALEFONT, FALSE8); temp = g_theOptionsManager->CalculateStringWidth(quit); g_theOptionsManager->DisplayText(ad, pitch, quit, halfScreen - (temp / 2), 200, PALEFONT, FALSE8); surface_manager->Unlock_surface(working_buffer_id); // Update screen manually surface_manager->Flip(); for (uint32 tik = 0; tik < CD_SEARCH_DELAY; tik++) { // Check for the CD again if (CheckForCD(number)) { // Cool, got it // Black out screen manually surface_manager->Clear_surface(working_buffer_id); surface_manager->Flip(); gotCD = TRUE8; break; } } checking = FALSE8; } // we don't use this function so SendMessage is just disabled //SendMessage(windowHandle, WM_PAINT, 0, 0); if (gotCD) break; ad = surface_manager->Lock_surface(working_buffer_id); pitch = surface_manager->Get_pitch(working_buffer_id); // Display message g_theOptionsManager->DisplayText(ad, pitch, msg, 0, 90, NORMALFONT, TRUE8, TRUE8); // Display choices temp = g_theOptionsManager->CalculateStringWidth(ok); g_theOptionsManager->DisplayText(ad, pitch, ok, halfScreen - (temp / 2), 130, (bool8)(selection == 0) ? SELECTEDFONT : NORMALFONT, FALSE8); temp = g_theOptionsManager->CalculateStringWidth(cancel); g_theOptionsManager->DisplayText(ad, pitch, cancel, halfScreen - (temp / 2), 160, (bool8)(selection == 1) ? SELECTEDFONT : NORMALFONT, FALSE8); temp = g_theOptionsManager->CalculateStringWidth(quit); g_theOptionsManager->DisplayText(ad, pitch, quit, halfScreen - (temp / 2), 200, (bool8)(selection == 2) ? SELECTEDFONT : NORMALFONT, FALSE8); surface_manager->Unlock_surface(working_buffer_id); // Update screen manually surface_manager->Flip(); } #endif return FALSE8; } void ClusterManager::LoadFileList(MISSION_ID /*mission*/) { if (m_minimumInstall == FALSE8) return; #if 0 // Unload if we need to if (m_filelistCursor != -1) { memset(m_theList, 0, MAX_BYTESIZE_OF_A_FILELIST); m_filelistCursor = -1; } // Make path to the correct filelist (on the CD) pxString filelist; HashFile(mission_names[mission], m_missionDir); filelist.Format("%sm\\%s\\filelist.txt", GetCDRoot(), m_missionDir); // Cool, now find out how big it is m_filelistSize = GetFileSize(filelist); if (m_filelistSize == 0) Fatal_error(pxVString("Couldn't get size of %s", (const char *)filelist)); // Less four bytes for the total byte count m_filelistSize -= 4; // Open the file in binary mode FILE *fp = openDiskFileForBinaryRead(filelist); if (fp == NULL) Fatal_error(pxVString("Couldn't open file %s for reading", (const char *)filelist)); // Read the total byte count if (fread(&m_filelistTotalBytes, 1, 4, fp) != 4) Fatal_error("Failed to read total byte count from filelist"); // Read that puppy in if (fread(m_theList, 1, m_filelistSize, fp) != (uint32)m_filelistSize) Fatal_error("Failed to read total byte count from filelist"); fclose(fp); // Reset our cursor m_filelistCursor = 0; // Get pointer to the list in memory char *ptr = (char *)m_theList; // Now change all the newline markers to terminators while (ptr[m_filelistCursor]) { // New line encountered if (ptr[m_filelistCursor] == 0x0d) { // Overwrite with terminators ptr[m_filelistCursor] = 0; ptr[m_filelistCursor + 1] = 0; m_filelistCursor++; } m_filelistCursor++; } // Reset our cursor m_filelistCursor = 0; #endif } char *ClusterManager::GetFileListEntry() { if (m_filelistCursor == -1) Fatal_error("Can't retrieve filelist entry without loading a filelist first!"); char *line = NULL; // End of file check if (m_filelistCursor < m_filelistSize) { line = &(m_theList[m_filelistCursor]); // Move to next line m_filelistCursor += strlen((const char *)&(m_theList[m_filelistCursor])); // Skip any terminators to get to the start of the next line while (m_theList[m_filelistCursor] == 0) m_filelistCursor++; } return line; } bool8 ClusterManager::DrawCoverFrame(void) { // Draw a frame of the torture movie g_while_u_wait_SequenceManager->drawFrame(working_buffer_id); // Have we finished both movie playback and mission install if (m_installDone) { // Release bink from playing the movie g_while_u_wait_SequenceManager->kill(); // Free up resources delete[] m_movieMemoryPointer; // Quit only when movie has finished return FALSE8; } DrawProgressBits(); // Update screen manually surface_manager->Flip(); return TRUE8; } void ClusterManager::InitialiseProgressBits() { // Tweakable int width = 5; int height = 15; int spacing = 2; int initialY = SCREEN_DEPTH - height - 30; // Calculate entire width so we can centre things int length = (NUMBER_OF_PROGRESS_BITS * (width + spacing)) - spacing; int initialX = (SCREEN_WIDTH / 2) - (length / 2); for (int i = 0; i < NUMBER_OF_PROGRESS_BITS; i++) { m_progressBits[i].r.left = initialX; m_progressBits[i].r.top = initialY; m_progressBits[i].r.right = initialX + width; m_progressBits[i].r.bottom = initialY + height; m_progressBits[i].state = 0; // Now increment initialX += width + spacing; } m_bitsDone = 0; } void ClusterManager::UpdateProgressBits() { if (m_frameCounter % PROGRESS_BIT_DELAY == 0) { // Update the state of all bits for (int i = 0; i < NUMBER_OF_PROGRESS_BITS; i++) { if (m_progressBits[i].state > 0 && m_progressBits[i].state != 6) { m_progressBits[i].state = m_progressBits[i].state + 1; } } } // Bytes per progress bit float progress_inc = (float)(m_filelistTotalBytes / NUMBER_OF_PROGRESS_BITS); // The number of bits that should bit switched on uint bitsOn = (int)((float)m_bytesDone / progress_inc); // Do we need to switch on a new bit if (bitsOn > m_bitsDone) { m_progressBits[m_bitsDone].state = 1; m_bitsDone++; } } void ClusterManager::DrawProgressBits() { for (int i = 0; i < NUMBER_OF_PROGRESS_BITS; i++) { 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]); } } void ClusterManager::Shutdown(void) { // Only works on minimum install CleanHardDisk(); } void RecursivelyDeleteDirectory(const char * /*path*/) { warning("STUB: RecursivelyDeleteDirectory()"); #if 0 // Parse this directory DirectorySearch search(path); // While there are things in the directory while (search.GetNextFile()) { // Is this a file or a directory if (search.GetFileType() & _A_SUBDIR) { pxString subdir = search.GetPathName(); // Recurse on this directory RecursivelyDeleteDirectory(subdir); // Then remove it too // Ensure write access _chmod(subdir, _S_IWRITE); // Delete subdirectory if (rmdir(subdir) == -1) { int lastChar = subdir.GetLen() - 1; // Need to chop of slash character if (subdir[lastChar] == '\\') { subdir[lastChar] = '\0'; // Try again if (rmdir(subdir) == -1) { // Couldn't delete this! Fatal_error(pxVString("Couldn't delete directory: %s", (const char *)subdir)); } } else { // Couldn't delete this! Fatal_error(pxVString("Couldn't delete directory: %s", (const char *)subdir)); } } } else { // Need to make sure that files aren't read-only first _chmod(search.GetPathName(), _S_IWRITE); // Delete file if (_unlink(search.GetPathName()) == -1) { // Couldn't delete this file MessageBox(NULL, pxVString("Couldn't delete file: %s", (const char *)search.GetPathName()), "ClusterManager Says:", MB_OK); } } } #endif } void ValidateDirectoryToDelete(const char *path) { if (strcmp(path, pxVString("m\\FP3YNHA\\")) == 0) return; else if (strcmp(path, pxVString("m\\HWYIPVA\\")) == 0) return; else if (strcmp(path, pxVString("m\\TPQUB4D\\")) == 0) return; else if (strcmp(path, pxVString("m\\RIGABTB\\")) == 0) return; else if (strcmp(path, pxVString("m\\GAIYO3A\\")) == 0) return; else if (strcmp(path, pxVString("m\\NMUFF0B\\")) == 0) return; else if (strcmp(path, pxVString("m\\1QYUOAA\\")) == 0) return; else if (strcmp(path, pxVString("m\\TT3WADD\\")) == 0) return; else Fatal_error(pxVString("ValidateDirectoryToDelete() failed on: %s", path)); } uint32 GetFileSize(const char *path) { Common::File file; if (!file.open(path)) { return 0; } return (uint32)file.size(); } const char *MissionIdToName(MISSION_ID mission) { switch (mission) { case MISSION1: return g_m01; case MISSION2: return g_m02; case MISSION3: return g_m03; case MISSION4: return g_m04; case MISSION5: return g_m05; case MISSION7: return g_m07; case MISSION8: return g_m08; case MISSION9: return g_m08; case MISSION10: return g_m10; } Fatal_error("MissionIdToName() should never get here - smack AndyB"); return NULL; } void MakeDirectoryTree(MISSION_ID /*mission*/) { warning("STUB: MakeDirectoryTree()"); #if 0 char pathToMake[128]; // First make the mission directory char missionDir[8]; HashFile(mission_names[mission], missionDir); sprintf(pathToMake, "m\\%s", missionDir); if (_mkdir(pathToMake) != 0) Fatal_error(pxVString("MakeDirectoryTree(): Failed to make directory: %s", pathToMake)); // Get the name of this mission char *mission_name = MissionIdToName(mission); // Now the session directories for (uint32 i = 0; i < MAX_SESSIONS; i += 2) { // Only build the session directories for this mission if (strcmp(mission_name, sessions[i]) == 0) { char sessionDir[8]; HashFile(sessions[i + 1], sessionDir); sprintf(pathToMake, "m\\%s\\%s", missionDir, sessionDir); if (_mkdir(pathToMake) != 0) Fatal_error(pxVString("MakeDirectoryTree(): Failed to make directory: %s", pathToMake)); } } #endif } } // End of namespace ICB