| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345 |
- /*
- ** 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/editormixfile.cpp $*
- * *
- * Author:: Patrick Smith *
- * *
- * $Modtime:: 9/12/01 12:38p $*
- * *
- * $Revision:: 7 $*
- * *
- *---------------------------------------------------------------------------------------------*
- * Functions: *
- * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- #include "stdafx.h"
- #include "filemgr.h"
- #include "editormixfile.h"
- #include "utils.h"
- #include "mixfile.h"
- #include "TGAToDXT.h"
- #include "LevelEdit.h"
- #include "RegKeys.h"
- #include <stdlib.h>
- #include <winbase.h>
- /////////////////////////////////////////////////////////////////////////
- //
- // EditorMixFileCreator
- //
- /////////////////////////////////////////////////////////////////////////
- EditorMixFileCreator::EditorMixFileCreator()
- {
- // OPTIMIZATION: Store state of texture compression for quick access.
- TexturesCompressed = Are_Textures_Compressed();
- }
- /////////////////////////////////////////////////////////////////////////
- //
- // Add_File
- //
- /////////////////////////////////////////////////////////////////////////
- void
- EditorMixFileCreator::Add_File (const char *full_path, const char *name)
- {
- char substitutefullpath [MAX_PATH];
- char substitutename [MAX_PATH];
-
- // See if another file needs to be substituted for the input file.
- Substitute_File (full_path, name, substitutefullpath, substitutename);
- StringClass lower_name = substitutename;
- ::strlwr (lower_name.Peek_Buffer ());
- //
- // Is this file already in the system?
- //
- EditorMixFileEntry *stored_path = FilenameHash.Get (lower_name);
- if (stored_path != NULL) {
-
- //
- // Don't store the new file unless its newer then the one already
- // in the hash
- //
- if (::Quick_Compare_Files (stored_path->Get_Path (), substitutefullpath) > 0) {
-
- //
- // Remap this entry to the new path
- //
- stored_path->Set_Path (substitutefullpath);
- }
- } else {
-
- //
- // Allocate a new entry...
- //
- stored_path = new EditorMixFileEntry;
- stored_path->Set_Name (lower_name);
- stored_path->Set_Path (substitutefullpath);
- //
- // Now add this entry to the hash
- //
- FilenameHash.Insert (lower_name, stored_path);
- }
- return ;
- }
- /////////////////////////////////////////////////////////////////////////
- //
- // Substitute_File
- //
- /////////////////////////////////////////////////////////////////////////
- void EditorMixFileCreator::Substitute_File (const char *fullpath, const char *name, char *substitutefullpath, char *substitutename)
- {
- bool substitutefile;
- char filename [_MAX_FNAME];
- char extension [_MAX_EXT];
- CString cachedfullpath;
- CString cachedname;
- // Assume that another file will not be substituted for the original file.
- substitutefile = false;
- // Has compression been enabled by user?
- if (TexturesCompressed) {
- // Is this file a .TGA? If so use a compressed (.DDS) version stored in the texture cache.
- _splitpath (fullpath, NULL, NULL, filename, extension);
- if (_stricmp (extension, ".tga") == 0) {
- const char *exclusionstring0 = "bump_";
- const char *exclusionstring1 = "font";
- // Does this filename not start with one of the exclusion strings?
- // NOTE 0: bump_*.* files are bump maps and cannot be compressed because the pixel data is interpreted as bump data.
- // NOTE 1: font*.* files are font files that are not processed by the run-time compressed texture loading system and, therefore, must be excluded.
- if ((_strnicmp (filename, exclusionstring0, strlen (exclusionstring0)) != 0) &&
- (_strnicmp (filename, exclusionstring1, strlen (exclusionstring1)) != 0)) {
- const char *ddsextension = ".dds";
- const char *failedtoopentext = "Failed to open %s\r\n";
- char mangleddirectory [_MAX_DIR];
- char mangledname [_MAX_FNAME];
- HANDLE tgafile;
- HANDLE ddsfile;
- char *s;
- // OPTIMIZATION: Has the .DDS file already been created and stored in the texture cache?
-
- // NOTE: If the name contains a subdirectory mangle it with the filename. This will ensure that the filename is unique.
- _splitpath (name, NULL, mangleddirectory, mangledname, NULL);
-
- // NOTE: The cached name must be the same as the original name but with .DDS extension.
- cachedname = mangleddirectory;
- cachedname += mangledname;
- cachedname += ddsextension;
- // Replace backslashes in the mangled directory with dots to 'flatten' it.
- s = mangleddirectory;
- while (*s != '\0') {
- if ((*s == '\\') || (*s == '/')) {
- *s = '.';
- }
- s++;
- }
- cachedfullpath = ::Get_File_Mgr()->Get_Texture_Cache_Path();
- cachedfullpath += "\\";
- cachedfullpath += mangleddirectory;
- cachedfullpath += filename;
- cachedfullpath += ddsextension;
- // Does the cached .DDS file exist?
- tgafile = CreateFile (fullpath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0L, NULL);
- ddsfile = CreateFile (cachedfullpath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0L, NULL);
- if (tgafile != INVALID_HANDLE_VALUE) {
-
- FILETIME tgawritetime;
- GetFileTime (tgafile, NULL, NULL, &tgawritetime);
- if (ddsfile != INVALID_HANDLE_VALUE) {
- FILETIME ddswritetime;
- // Does the cached .DDS file have the same time stamp as the .TGA file?
- GetFileTime (ddsfile, NULL, NULL, &ddswritetime);
- CloseHandle (tgafile);
- CloseHandle (ddsfile);
- if ((tgawritetime.dwLowDateTime == ddswritetime.dwLowDateTime) && (tgawritetime.dwHighDateTime == ddswritetime.dwHighDateTime)) {
- // Files have the same time stamp. Substitute the .DDS file.
- substitutefile = true;
-
- } else {
- // Files have a different time stamp (and therefore it is assumed that the .TGA file has been modified since the .DDS file was created).
- // Create a new .DDS file from the .TGA file and give it the same time stamp as the .TGA file.
- substitutefile = Convert_File (fullpath, cachedfullpath, &tgawritetime);
- }
-
- } else {
- CloseHandle (tgafile);
- // Create a new .DDS file from the .TGA file and give it the same time stamp as the .TGA file.
- substitutefile = Convert_File (fullpath, cachedfullpath, &tgawritetime);
- }
- } else {
- CString message;
-
- // Cannot open the input file!
- message.Format ("Failed to open: %s\r\n", fullpath);
- ::Output_Message (message);
- }
- }
- }
- }
- if (substitutefile) {
- // Substitute the cached file.
- strcpy (substitutefullpath, cachedfullpath);
- strcpy (substitutename, cachedname);
- } else {
- // Refer the caller back to the original input file ie. no file is substituted.
- strcpy (substitutefullpath, fullpath);
- strcpy (substitutename, name);
- }
- }
- /////////////////////////////////////////////////////////////////////////
- //
- // Generate_Mix_File
- //
- /////////////////////////////////////////////////////////////////////////
- bool EditorMixFileCreator::Convert_File (const char *fullpath, const char *cachedfullpath, FILETIME *tgawritetimeptr)
- {
- const char *failedtocompresstext = "Failed to compress %s\r\n";
- const char *removedalphatext = "Removed alpha channel in %s\r\n";
- bool success, alpharemoved;
- CString message;
-
- success = _TGAToDXTConverter.Convert (fullpath, cachedfullpath, tgawritetimeptr, alpharemoved);
- if (!success) {
- message.Format (failedtocompresstext, fullpath);
- ::Output_Message (message);
- }
- if (alpharemoved) {
- message.Format (removedalphatext, fullpath);
- ::Output_Message (message);
- }
- return (success);
- }
- /////////////////////////////////////////////////////////////////////////
- //
- // Generate_Mix_File
- //
- /////////////////////////////////////////////////////////////////////////
- void
- EditorMixFileCreator::Generate_Mix_File (const char *full_path)
- {
- MixFileCreator mix_file (full_path);
- //
- // Loop over all the entries in hash table
- //
- HashTemplateIterator<StringClass, EditorMixFileEntry *> iterator (FilenameHash);
- for (iterator.First (); iterator.Is_Done () == false; iterator.Next ()) {
- EditorMixFileEntry *entry = iterator.Peek_Value ();
-
- //
- // Add this file to the mix
- //
- mix_file.Add_File (entry->Get_Path (), entry->Get_Name ());
- }
- return ;
- }
- /////////////////////////////////////////////////////////////////////////
- //
- // Flush
- //
- /////////////////////////////////////////////////////////////////////////
- void
- EditorMixFileCreator::Flush (void)
- {
- //
- // Loop over all the entries in hash table
- //
- HashTemplateIterator<StringClass, EditorMixFileEntry *> iterator (FilenameHash);
- for (iterator.First (); iterator.Is_Done () == false; iterator.Next ()) {
-
- //
- // Free this entry
- //
- EditorMixFileEntry *entry = iterator.Peek_Value ();
- delete entry;
- }
- FilenameHash.Remove_All ();
- return ;
- }
- /////////////////////////////////////////////////////////////////////////
- //
- // Set_Texture_Compression
- //
- /////////////////////////////////////////////////////////////////////////
- void EditorMixFileCreator::Set_Texture_Compression (bool onoff)
- {
- // Update the registry.
- theApp.WriteProfileInt (CONFIG_KEY, TEXTURE_COMPRESSION_VALUE, onoff);
- }
- /////////////////////////////////////////////////////////////////////////
- //
- // Are_Textures_Compressed
- //
- /////////////////////////////////////////////////////////////////////////
- bool EditorMixFileCreator::Are_Textures_Compressed()
- {
- // Read from the registry.
- return (theApp.GetProfileInt (CONFIG_KEY, TEXTURE_COMPRESSION_VALUE, 0) == 1);
- }
|