| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633 |
- /*
- ** Command & Conquer Renegade(tm)
- ** Copyright 2025 Electronic Arts Inc.
- **
- ** 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 3 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, see <http://www.gnu.org/licenses/>.
- */
- /***********************************************************************************************
- *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
- ***********************************************************************************************
- * *
- * Project Name : leveledit *
- * *
- * $Archive:: /Commando/Code/Tools/LevelEdit/mixfiledatabase.cpp $*
- * *
- * Author:: Patrick Smith *
- * *
- * $Modtime:: 6/19/02 3:58p $*
- * *
- * $Revision:: 5 $*
- * *
- *---------------------------------------------------------------------------------------------*
- * Functions: *
- * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- #include "stdafx.h"
- #include "mixfiledatabase.h"
- #include "registry.h"
- #include "utils.h"
- #include "mixfile.h"
- #include "rawfile.h"
- #include "shlwapi.h"
- //////////////////////////////////////////////////////////////////////
- // Static member initialization
- //////////////////////////////////////////////////////////////////////
- MixFileDatabaseClass *MixFileDatabaseClass::_TheInstance = NULL;
- //////////////////////////////////////////////////////////////////////
- //
- // MixFileDatabaseClass
- //
- //////////////////////////////////////////////////////////////////////
- MixFileDatabaseClass::MixFileDatabaseClass (void)
- {
- //
- // Open Renegade's registry
- //
- const char * const RENEGADE_REG_KEY = "Software\\Westwood\\Renegade";
- RegistryClass registry (RENEGADE_REG_KEY);
- if (registry.Is_Valid ()) {
- //
- // Read the installation path from the registry
- //
- StringClass install_path;
- const char * const RENEGADE_INSTALL_VALUE = "InstallPath";
- registry.Get_String (RENEGADE_INSTALL_VALUE, install_path);
- if (install_path.Get_Length () > 0) {
- //
- // The mix files are contained in the data sub-directory
- //
- install_path = ::Strip_Filename_From_Path (install_path);
- MixFilePath = ::Make_Path (install_path, "DATA");
- }
- }
- _TheInstance = this;
- return ;
- }
- //////////////////////////////////////////////////////////////////////
- //
- // ~MixFileDatabaseClass
- //
- //////////////////////////////////////////////////////////////////////
- MixFileDatabaseClass::~MixFileDatabaseClass (void)
- {
- _TheInstance = NULL;
- return ;
- }
- //////////////////////////////////////////////////////////////////////
- //
- // Open_Database
- //
- //////////////////////////////////////////////////////////////////////
- bool
- MixFileDatabaseClass::Open_Database
- (
- LPCTSTR ini_filename,
- LPCTSTR username,
- LPCTSTR password
- )
- {
- //
- // Add the always mix files to the file factory list
- //
- CString always_dbs_path = ::Make_Path (MixFilePath, "Always.dbs");
- CString always_dat_path = ::Make_Path (MixFilePath, "Always.dat");
- MainFileFactory.Add_FileFactory (new MixFileFactoryClass (always_dbs_path, &RenegadeDataFileFactory), "always.dat");
- MainFileFactory.Add_FileFactory (new MixFileFactoryClass (always_dat_path, &RenegadeDataFileFactory), "always.dbs");
- //
- // Build a search path for mix files in the data directory
- //
- CString search_path = ::Make_Path (MixFilePath, "*.mix");
- //
- // Search for all mix files in the installation directory
- //
- WIN32_FIND_DATA find_info = { 0 };
- BOOL keep_going = TRUE;
- HANDLE file_find = NULL;
- for (file_find = ::FindFirstFile (search_path, &find_info);
- (file_find != INVALID_HANDLE_VALUE) && keep_going;
- keep_going = ::FindNextFile (file_find, &find_info))
- {
- //
- // Add this mix file to our mix file factory list
- //
- CString full_path = ::Make_Path (MixFilePath, find_info.cFileName);
- MainFileFactory.Add_FileFactory (new MixFileFactoryClass (full_path, &RenegadeDataFileFactory), find_info.cFileName);
- }
- //
- // Close the search handle
- //
- if (file_find != INVALID_HANDLE_VALUE) {
- ::FindClose (file_find);
- }
- return true;
- }
- //////////////////////////////////////////////////////////////////////
- //
- // Check_Out
- //
- //////////////////////////////////////////////////////////////////////
- bool
- MixFileDatabaseClass::Check_Out (LPCTSTR local_filename, bool get_locally)
- {
- bool retval = true;
- //
- // Simply do a get as necessary
- //
- if (get_locally) {
- retval = Get (local_filename);
- }
- return retval;
- }
- //////////////////////////////////////////////////////////////////////
- //
- // Get
- //
- //////////////////////////////////////////////////////////////////////
- bool
- MixFileDatabaseClass::Get (LPCTSTR local_filename)
- {
- StringClass filename;
- Get_Filename (local_filename, filename);
- StringClass full_local_path = local_filename;
- //
- // Check to see if we need to swap texture extensions
- //
- if (Internal_Does_File_Exist (filename) == false && Is_Texture (filename)) {
- Swap_Texture_Extension (filename);
-
- StringClass local_path = (const char *)::Strip_Filename_From_Path (local_filename);
- full_local_path = ::Make_Path (local_path, filename);
- }
- //
- // Get the file
- //
- return Internal_Get (filename, full_local_path);
- }
- //////////////////////////////////////////////////////////////////////
- //
- // Internal_Get
- //
- //////////////////////////////////////////////////////////////////////
- bool
- MixFileDatabaseClass::Internal_Get (LPCTSTR filename, LPCSTR local_path)
- {
- bool retval = false;
- //
- // Get the file from one of the mix files
- //
- FileClass *file = MainFileFactory.Get_File (filename);
- if (file != NULL) {
- //
- // Copy the file if it exists
- //
- if (file->Is_Available () && file->Open ()) {
- retval = Copy_File (file, local_path);
- file->Close ();
- }
- MainFileFactory.Return_File (file);
- }
- return retval;
- }
- //////////////////////////////////////////////////////////////////////
- //
- // Copy_File
- //
- //////////////////////////////////////////////////////////////////////
- bool
- MixFileDatabaseClass::Copy_File (FileClass *src_file, LPCTSTR local_filename)
- {
- if ( src_file == NULL || local_filename == NULL ||
- ::GetFileAttributes (local_filename) != 0xFFFFFFFF)
- {
- return false;
- }
- bool retval = false;
- //
- // Ensure the directory structure exists before we attempt to create the file
- //
- StringClass path = (const char *)::Strip_Filename_From_Path (local_filename);
- Create_Directory_Structure (path);
- //
- // Create the destination file
- //
- RawFileClass dest_file;
- dest_file.Set_Name (local_filename);
- if (dest_file.Open (RawFileClass::WRITE)) {
- retval = true;
- //
- // Copy the data from the source file to the destination file
- //
- int file_size = src_file->Size ();
- uint8 buffer[4096];
- while (file_size > 0) {
-
- //
- // Read the data from the source file
- //
- int bytes = min (file_size, (int)sizeof (buffer));
- int copied_size = src_file->Read (buffer, bytes);
- file_size -= copied_size;
- if (copied_size <= 0) {
- break;
- }
-
- //
- // Copy the data to the dest file (kick out of the loop on error)
- //
- if (dest_file.Write (buffer, copied_size) != copied_size) {
- break;
- }
- }
- //
- // Close the destination file
- //
- dest_file.Close ();
- }
- return retval;
- }
- //////////////////////////////////////////////////////////////////////
- //
- // Does_File_Exist
- //
- //////////////////////////////////////////////////////////////////////
- bool
- MixFileDatabaseClass::Does_File_Exist (LPCTSTR local_filename)
- {
- bool retval = false;
- //
- // Test to see if the file exists
- //
- StringClass filename;
- Get_Filename (local_filename, filename);
- retval = Internal_Does_File_Exist (filename);
- //
- // If the file did not exists, check to see if its a texture...
- //
- if (retval == false && Is_Texture (filename)) {
-
- //
- // Check to see if either the compressed or uncompressed
- // texture exists
- //
- Swap_Texture_Extension (filename);
- retval = Internal_Does_File_Exist (filename);
- }
- return retval;
- }
- //////////////////////////////////////////////////////////////////////
- //
- // Internal_Does_File_Exist
- //
- //////////////////////////////////////////////////////////////////////
- bool
- MixFileDatabaseClass::Internal_Does_File_Exist (LPCSTR filename)
- {
- bool retval = false;
- //
- // Get the file from one of the mix files
- //
- FileClass *file = MainFileFactory.Get_File (filename);
- if (file != NULL) {
- //
- // Does the file exist?
- //
- if (file->Is_Available ()) {
- retval = true;
- }
- MainFileFactory.Return_File (file);
- }
- return retval;
- }
- //////////////////////////////////////////////////////////////////////////////////
- //
- // Create_Directory_Structure
- //
- //////////////////////////////////////////////////////////////////////////////////
- void
- MixFileDatabaseClass::Create_Directory_Structure (LPCTSTR path)
- {
- if (path != NULL && path[0] != 0 && ::GetFileAttributes (path) == 0xFFFFFFFF) {
- Create_Directory_Structure (::Strip_Filename_From_Path (path));
- ::CreateDirectory (path, NULL);
- }
-
- return ;
- }
- //////////////////////////////////////////////////////////////////////////////////
- //
- // Is_Texture
- //
- //////////////////////////////////////////////////////////////////////////////////
- bool
- MixFileDatabaseClass::Is_Texture (LPCSTR filename)
- {
- StringClass temp_str (filename, true);
- ::strlwr (temp_str.Peek_Buffer ());
- //
- // Check to see if this is either a compressed or uncompressed texture
- //
- bool retval = false;
- if (::strstr (temp_str.Peek_Buffer (), ".tga") || ::strstr (temp_str.Peek_Buffer (), ".dds")) {
- retval = true;
- }
- return retval;
- }
- //////////////////////////////////////////////////////////////////////////////////
- //
- // Swap_Texture_Extension
- //
- //////////////////////////////////////////////////////////////////////////////////
- void
- MixFileDatabaseClass::Swap_Texture_Extension (StringClass &filename)
- {
- ::strlwr (filename.Peek_Buffer ());
- //
- // Is this a tga file (uncompressed), or a dds file (compressed)?
- //
- char *tga_extension = ::strstr (filename.Peek_Buffer (), ".tga");
- if (tga_extension != NULL) {
-
- //
- // Simply copy the new extension into the string
- //
- ::strcpy (tga_extension, ".dds");
- } else {
- char *dds_extension = ::strstr (filename.Peek_Buffer (), ".dds");
- if (dds_extension != NULL) {
- //
- // Simply copy the new extension into the string
- //
- ::strcpy (dds_extension, ".tga");
- }
- }
- return ;
- }
- //////////////////////////////////////////////////////////////////////////////////
- //
- // Find_Files
- //
- //////////////////////////////////////////////////////////////////////////////////
- void
- MixFileDatabaseClass::Find_Files (DynamicVectorClass<StringClass> &file_list, LPCTSTR search_mask)
- {
- //
- // Loop over all the factories
- //
- int factory_count = MainFileFactory.Get_Factory_Count ();
- for (int index = 0; index < factory_count; index ++) {
-
- //
- // Get a pointer to the current factory
- //
- FileFactoryClass *factory = MainFileFactory.Get_Factory (index);
- if (factory != NULL) {
-
- //
- // We assume that this is a mix file factory (since all the factories
- // we're adding are mix file factories). Note: This can easily
- // break.
- //
- MixFileFactoryClass *mix_factory = static_cast<MixFileFactoryClass *> (factory);
- //
- // Get a list of all the files in this mix file
- //
- DynamicVectorClass<StringClass> files_in_mix;
- files_in_mix.Set_Growth_Step (1000);
- mix_factory->Build_Filename_List (files_in_mix);
- //
- // Now, add any files that match the supplied wildcard to our master list
- //
- for (int file_index = 0; file_index < files_in_mix.Count (); file_index ++) {
- const char *filename = files_in_mix[file_index];
- //
- // Add this file to our list if it matches the mask
- //
- if (::PathMatchSpec (filename, search_mask)) {
- file_list.Add (filename);
- }
- }
- }
- }
- return ;
- }
- //////////////////////////////////////////////////////////////////////////////////
- //
- // Get_All
- //
- //////////////////////////////////////////////////////////////////////////////////
- bool
- MixFileDatabaseClass::Get_All (LPCTSTR dest_path, LPCTSTR search_mask)
- {
- DynamicVectorClass<StringClass> file_list;
- //
- // Loop over all the factories
- //
- int factory_count = MainFileFactory.Get_Factory_Count ();
- for (int index = 0; index < factory_count; index ++) {
-
- //
- // Get a pointer to the current factory
- //
- FileFactoryClass *factory = MainFileFactory.Get_Factory (index);
- if (factory != NULL) {
-
- //
- // We assume that this is a mix file factory (since all the factories
- // we're adding are mix file factories). Note: This can easily
- // break.
- //
- MixFileFactoryClass *mix_factory = static_cast<MixFileFactoryClass *> (factory);
- //
- // Get a list of all the files in this mix file
- //
- DynamicVectorClass<StringClass> files_in_mix;
- files_in_mix.Set_Growth_Step (1000);
- mix_factory->Build_Filename_List (files_in_mix);
- //
- // Now, add any files that match the supplied wildcard to our master list
- //
- for (int file_index = 0; file_index < files_in_mix.Count (); file_index ++) {
- const char *filename = files_in_mix[file_index];
- //
- // Add this file to our list if it matches the mask
- //
- if (::PathMatchSpec (filename, search_mask)) {
- file_list.Add (filename);
- }
- }
- }
- }
- //
- // Now, get all the matching files to the specified path
- //
- for (index = 0; index < file_list.Count (); index ++) {
- StringClass full_path = (const char *)::Make_Path (dest_path, file_list[index]);
- Get (full_path);
- }
- return true;
- }
- ///////////////////////////////////////////////////////////////////
- //
- // Get_File
- //
- ///////////////////////////////////////////////////////////////////
- FileClass *
- MixFileDatabaseClass::Get_File (LPCTSTR local_filename)
- {
- FileClass *retval = NULL;
- if (::GetFileAttributes (local_filename) != 0xFFFFFFFF) {
- //
- // Do this to get around an oddity of the class when you pass in
- // the filename to the constructor
- //
- retval = new RawFileClass;
- retval->Set_Name (local_filename);
- } else {
-
- //
- // Get a pointer to the file in the mix file
- //
- StringClass filename;
- Get_Filename (local_filename, filename);
- retval = MainFileFactory.Get_File (filename);
- }
- return retval;
- }
- ///////////////////////////////////////////////////////////////////
- //
- // Get_Filename
- //
- ///////////////////////////////////////////////////////////////////
- void
- MixFileDatabaseClass::Get_Filename (LPCTSTR path, StringClass &filename)
- {
- filename = path;
- ::strlwr (filename.Peek_Buffer ());
- //
- // Check to see if the sub-directory is important, if it is, then
- // return the sub-directory as well
- //
- char *subdir_token = ::strstr (filename, "+\\");
- if (subdir_token != NULL) {
- //
- // Try to find the preceeding directory delimiter
- //
- int index = (subdir_token - filename.Peek_Buffer ());
- for (; index >= 0; index --) {
- if (filename[index] == '\\') {
- StringClass temp_str = filename;
- filename = (temp_str.Peek_Buffer () + index + 1);
- break;
- }
- }
- } else {
- //
- // Simply strip the filename from the path
- //
- filename = (const char *)::Get_Filename_From_Path (filename);
- }
- return ;
- }
|