diff --git a/Core/GameEngine/Include/Common/GameDefines.h b/Core/GameEngine/Include/Common/GameDefines.h index 189e5b07a3..4d11397296 100644 --- a/Core/GameEngine/Include/Common/GameDefines.h +++ b/Core/GameEngine/Include/Common/GameDefines.h @@ -73,6 +73,12 @@ #define ENABLE_FILESYSTEM_EXISTENCE_CACHE (1) #endif +// Enable prioritization of textures by size. This will improve the texture quality of 481 textures in Zero Hour +// by using the larger resolution textures from Generals. Content wise these textures are identical. +#ifndef PRIORITIZE_TEXTURES_BY_SIZE +#define PRIORITIZE_TEXTURES_BY_SIZE (1) +#endif + // Enable obsolete code. This mainly refers to code that existed in Generals but was removed in GeneralsMD. // Disable and remove this when Generals and GeneralsMD are merged. #if RTS_GENERALS diff --git a/Generals/Code/GameEngineDevice/Include/W3DDevice/GameClient/W3DFileSystem.h b/Generals/Code/GameEngineDevice/Include/W3DDevice/GameClient/W3DFileSystem.h index cc96045c8a..1cd1742675 100644 --- a/Generals/Code/GameEngineDevice/Include/W3DDevice/GameClient/W3DFileSystem.h +++ b/Generals/Code/GameEngineDevice/Include/W3DDevice/GameClient/W3DFileSystem.h @@ -40,6 +40,7 @@ #define __W3DFILESYSTEM_H_ #include "WWLib/ffactory.h" +#include "Common/ArchiveFileSystem.h" #include "Common/file.h" //------------------------------------------------------------------------------------------------- @@ -93,6 +94,11 @@ class W3DFileSystem : public FileFactoryClass { virtual FileClass * Get_File( char const *filename ); virtual void Return_File( FileClass *file ); + +private: + + static void reprioritizeTexturesBySize(); + static void reprioritizeTexturesBySize(ArchivedDirectoryInfo& dirInfo); }; extern W3DFileSystem *TheW3DFileSystem; diff --git a/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DFileSystem.cpp b/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DFileSystem.cpp index 8b61540156..1870b062d9 100644 --- a/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DFileSystem.cpp +++ b/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DFileSystem.cpp @@ -40,6 +40,7 @@ // for now we maintain old legacy files // #define MAINTAIN_LEGACY_FILES +#include "Common/ArchiveFile.h" #include "Common/Debug.h" #include "Common/file.h" #include "Common/FileSystem.h" @@ -47,10 +48,11 @@ #include "Common/MapObject.h" #include "Common/Registry.h" #include "W3DDevice/GameClient/W3DFileSystem.h" -// DEFINES //////////////////////////////////////////////////////////////////////////////////////// #include +// DEFINES //////////////////////////////////////////////////////////////////////////////////////// + //------------------------------------------------------------------------------------------------- /** Game file access. At present this allows us to access test assets, assets from * legacy GDI assets, and the current flat directory access for textures, models etc */ @@ -71,9 +73,9 @@ typedef enum GameFileClass::GameFileClass( char const *filename ) { - m_fileExists = FALSE; m_theFile = NULL; - m_filePath[ 0 ] = 0; + m_fileExists = FALSE; + m_filePath[0] = 0; m_filename[0] = 0; if( filename ) @@ -119,6 +121,24 @@ inline static Bool isImageFileType( GameFileType fileType ) return (fileType == FILE_TYPE_TGA || fileType == FILE_TYPE_DDS); } +//------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------- +static GameFileType getFileType( char const *filename ) +{ + if (char const *extension = strrchr( filename, '.' )) + { + // test the extension to recognize a few key file types + if( stricmp( extension, ".w3d" ) == 0 ) + return FILE_TYPE_W3D; + else if( stricmp( extension, ".tga" ) == 0 ) + return FILE_TYPE_TGA; + else if( stricmp( extension, ".dds" ) == 0 ) + return FILE_TYPE_DDS; + } + + return FILE_TYPE_COMPLETELY_UNKNOWN; // MBL FILE_TYPE_UNKNOWN change due to compile error +} + //------------------------------------------------------------------------------------------------- /** Sets the file name, and finds the GDI asset if present. */ //------------------------------------------------------------------------------------------------- @@ -131,41 +151,7 @@ char const * GameFileClass::Set_Name( char const *filename ) // save the filename strlcpy( m_filename, filename, _MAX_PATH ); - char name[_MAX_PATH]; - const Int EXT_LEN = 32; - char extension[EXT_LEN]; - extension[0] = 0; - strcpy(name, filename); - Int i = strlen(name); - i--; - Int extLen = 1; - while(i>0 && extLen < EXT_LEN) { - if (name[i] == '.') { - strcpy(extension, name+i); - name[i] = 0; - break; - } - i--; - extLen++; - } - Int j = 0; - // Strip out spaces. - for (i=0; name[i]; i++) { - if (name[i] != ' ') { - name[j] = name[i]; - j++; - } - } - name[j] = 0; - - // test the extension to recognize a few key file types - GameFileType fileType = FILE_TYPE_COMPLETELY_UNKNOWN; // MBL FILE_TYPE_UNKNOWN change due to compile error - if( stricmp( extension, ".w3d" ) == 0 ) - fileType = FILE_TYPE_W3D; - else if( stricmp( extension, ".tga" ) == 0 ) - fileType = FILE_TYPE_TGA; - else if( stricmp( extension, ".dds" ) == 0 ) - fileType = FILE_TYPE_DDS; + GameFileType fileType = getFileType(filename); // all .w3d files are in W3D_DIR_PATH, all .tga files are in TGA_DIR_PATH if( fileType == FILE_TYPE_W3D ) @@ -250,7 +236,7 @@ char const * GameFileClass::Set_Name( char const *filename ) strlcat(m_filePath, filename, ARRAY_SIZE(m_filePath)); } - if( isImageFileType(fileType) ) + else if( isImageFileType(fileType) ) { sprintf(m_filePath,USER_TGA_DIR_PATH, TheGlobalData->getPath_UserData().str()); //strcpy( m_filePath, USER_TGA_DIR_PATH ); @@ -264,10 +250,10 @@ char const * GameFileClass::Set_Name( char const *filename ) } - // We Need to be able to "temporarily copy over the map preview for whichever directory it came from + // We need to be able to temporarily copy over the map preview for whichever directory it came from if( m_fileExists == FALSE && TheGlobalData) { - if( fileType == FILE_TYPE_TGA ) // just TGA, since we don't dds previews + if( fileType == FILE_TYPE_TGA ) // just TGA, since we don't do dds previews { sprintf(m_filePath,MAP_PREVIEW_DIR_PATH, TheGlobalData->getPath_UserData().str()); //strcpy( m_filePath, USER_TGA_DIR_PATH ); @@ -343,11 +329,9 @@ int GameFileClass::Open(int rights) return(false); } - // just open up the file in m_filePath m_theFile = TheFileSystem->openFile( m_filePath, File::READ | File::BINARY ); return (m_theFile != NULL); - } //------------------------------------------------------------------------------------------------- @@ -418,12 +402,16 @@ void GameFileClass::Close(void) extern W3DFileSystem *TheW3DFileSystem = NULL; //------------------------------------------------------------------------------------------------- -/** Constructor. Creating an instance of this class overrices the default +/** Constructor. Creating an instance of this class overrides the default W3D file factory. */ //------------------------------------------------------------------------------------------------- W3DFileSystem::W3DFileSystem(void) { _TheFileFactory = this; // override the w3d file factory. + +#if RTS_ZEROHOUR && PRIORITIZE_TEXTURES_BY_SIZE + reprioritizeTexturesBySize(); +#endif } //------------------------------------------------------------------------------------------------- @@ -451,3 +439,79 @@ void W3DFileSystem::Return_File( FileClass *file ) delete file; } +//------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------- +void W3DFileSystem::reprioritizeTexturesBySize() +{ + ArchivedDirectoryInfo* dirInfo = TheArchiveFileSystem->friend_getArchivedDirectoryInfo(TGA_DIR_PATH); + if (dirInfo != NULL) + { + reprioritizeTexturesBySize(*dirInfo); + } +} + +//------------------------------------------------------------------------------------------------- +// TheSuperHackers @info This function moves the largest texture of its name to the front of the +// directory info. The algorithm only prioritizes the first item in the multimap, because this is +// what we currently need: +// Before: A(256kb) B(128kb) C(512kb) +// After: C(512kb) B(128kb) A(256kb) +// +// Catered to specific game archives only. This ensures that user created archives are not included +// for the re-prioritization of textures. +//------------------------------------------------------------------------------------------------- +void W3DFileSystem::reprioritizeTexturesBySize(ArchivedDirectoryInfo& dirInfo) +{ + const char* const superiorArchive = "Textures.big"; + const char* const inferiorArchive = "TexturesZH.big"; + + ArchivedFileLocationMap::iterator it0; + ArchivedFileLocationMap::iterator it1 = dirInfo.m_files.begin(); + ArchivedFileLocationMap::iterator end = dirInfo.m_files.end(); + + if (it1 != end) + { + it0 = it1; + ++it1; + } + + for (; it1 != end; ++it1) + { + const AsciiString& file0 = it0->first; + const AsciiString& file1 = it1->first; + + if (file0 == file1) + { + GameFileType type = getFileType(file0.str()); + if (isImageFileType(type)) + { + ArchiveFile* archive0 = it0->second; + ArchiveFile* archive1 = it1->second; + FileInfo info0; + FileInfo info1; + AsciiString filepath(dirInfo.m_path); + filepath.concat(file0); + + if (archive0->getFileInfo(filepath, &info0) && archive1->getFileInfo(filepath, &info1)) + { + if (info0.size() < info1.size() + && archive0->getName().endsWithNoCase(inferiorArchive) + && archive1->getName().endsWithNoCase(superiorArchive)) + { + std::swap(it0->second, it1->second); + +#if ENABLE_FILESYSTEM_LOGGING + DEBUG_LOG(("W3DFileSystem::reprioritizeTexturesBySize - prioritize %s(%ukb) from %s over %s(%ukb) from %s", + file1.str(), UnsignedInt(info1.size() / 1024), archive1->getName().str(), + file0.str(), UnsignedInt(info0.size() / 1024), archive0->getName().str())); +#endif + } + } + } + } + else + { + it0 = it1; + } + } +} diff --git a/GeneralsMD/Code/GameEngineDevice/Include/W3DDevice/GameClient/W3DFileSystem.h b/GeneralsMD/Code/GameEngineDevice/Include/W3DDevice/GameClient/W3DFileSystem.h index b5bbeda03a..a0ae96869d 100644 --- a/GeneralsMD/Code/GameEngineDevice/Include/W3DDevice/GameClient/W3DFileSystem.h +++ b/GeneralsMD/Code/GameEngineDevice/Include/W3DDevice/GameClient/W3DFileSystem.h @@ -40,6 +40,7 @@ #define __W3DFILESYSTEM_H_ #include "WWLib/ffactory.h" +#include "Common/ArchiveFileSystem.h" #include "Common/file.h" //------------------------------------------------------------------------------------------------- @@ -93,6 +94,11 @@ class W3DFileSystem : public FileFactoryClass { virtual FileClass * Get_File( char const *filename ); virtual void Return_File( FileClass *file ); + +private: + + static void reprioritizeTexturesBySize(); + static void reprioritizeTexturesBySize(ArchivedDirectoryInfo& dirInfo); }; extern W3DFileSystem *TheW3DFileSystem; diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DFileSystem.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DFileSystem.cpp index a4079ad8aa..eae8ffc250 100644 --- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DFileSystem.cpp +++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DFileSystem.cpp @@ -40,6 +40,7 @@ // for now we maintain old legacy files // #define MAINTAIN_LEGACY_FILES +#include "Common/ArchiveFile.h" #include "Common/Debug.h" #include "Common/file.h" #include "Common/FileSystem.h" @@ -47,10 +48,11 @@ #include "Common/MapObject.h" #include "Common/Registry.h" #include "W3DDevice/GameClient/W3DFileSystem.h" -// DEFINES //////////////////////////////////////////////////////////////////////////////////////// #include +// DEFINES //////////////////////////////////////////////////////////////////////////////////////// + //------------------------------------------------------------------------------------------------- /** Game file access. At present this allows us to access test assets, assets from * legacy GDI assets, and the current flat directory access for textures, models etc */ @@ -71,9 +73,9 @@ typedef enum GameFileClass::GameFileClass( char const *filename ) { - m_fileExists = FALSE; m_theFile = NULL; - m_filePath[ 0 ] = 0; + m_fileExists = FALSE; + m_filePath[0] = 0; m_filename[0] = 0; if( filename ) @@ -119,6 +121,24 @@ inline static Bool isImageFileType( GameFileType fileType ) return (fileType == FILE_TYPE_TGA || fileType == FILE_TYPE_DDS); } +//------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------- +static GameFileType getFileType( char const *filename ) +{ + if (char const *extension = strrchr( filename, '.' )) + { + // test the extension to recognize a few key file types + if( stricmp( extension, ".w3d" ) == 0 ) + return FILE_TYPE_W3D; + else if( stricmp( extension, ".tga" ) == 0 ) + return FILE_TYPE_TGA; + else if( stricmp( extension, ".dds" ) == 0 ) + return FILE_TYPE_DDS; + } + + return FILE_TYPE_COMPLETELY_UNKNOWN; // MBL FILE_TYPE_UNKNOWN change due to compile error +} + //------------------------------------------------------------------------------------------------- /** Sets the file name, and finds the GDI asset if present. @@ -146,43 +166,7 @@ char const * GameFileClass::Set_Name( char const *filename ) // save the filename strlcpy( m_filename, filename, _MAX_PATH ); - char name[_MAX_PATH]; - const Int EXT_LEN = 32; - char extension[EXT_LEN]; - extension[0] = 0; - strcpy(name, filename); - Int i = strlen(name); - i--; - Int extLen = 1; - while(i>0 && extLen < EXT_LEN) { - if (name[i] == '.') { - strcpy(extension, name+i); - name[i] = 0; - break; - } - i--; - extLen++; - } - Int j = 0; - // Strip out spaces. - for (i=0; name[i]; i++) { - if (name[i] != ' ') { - name[j] = name[i]; - j++; - } - } - name[j] = 0; - - // test the extension to recognize a few key file types - GameFileType fileType = FILE_TYPE_COMPLETELY_UNKNOWN; // MBL FILE_TYPE_UNKNOWN change due to compile error - if( stricmp( extension, ".w3d" ) == 0 ) - fileType = FILE_TYPE_W3D; - else if( stricmp( extension, ".tga" ) == 0 ) - fileType = FILE_TYPE_TGA; - else if( stricmp( extension, ".dds" ) == 0 ) - fileType = FILE_TYPE_DDS; - - + GameFileType fileType = getFileType(filename); // We need to be able to grab w3d's from a localization dir, since Germany hates exploding people units. if( fileType == FILE_TYPE_W3D ) @@ -192,9 +176,8 @@ char const * GameFileClass::Set_Name( char const *filename ) strlcat(m_filePath, filename, ARRAY_SIZE(m_filePath)); } - // We need to be able to grab images from a localization dir, because Art has a fetish for baked-in text. Munkee. - if( isImageFileType(fileType) ) + else if( isImageFileType(fileType) ) { static const char *localizedPathFormat = "Data/%s/Art/Textures/"; sprintf(m_filePath,localizedPathFormat, GetRegistryLanguage().str()); @@ -298,7 +281,7 @@ char const * GameFileClass::Set_Name( char const *filename ) strlcat(m_filePath, filename, ARRAY_SIZE(m_filePath)); } - if( isImageFileType(fileType) ) + else if( isImageFileType(fileType) ) { sprintf(m_filePath,USER_TGA_DIR_PATH, TheGlobalData->getPath_UserData().str()); //strcpy( m_filePath, USER_TGA_DIR_PATH ); @@ -312,10 +295,10 @@ char const * GameFileClass::Set_Name( char const *filename ) } - // We Need to be able to "temporarily copy over the map preview for whichever directory it came from + // We need to be able to temporarily copy over the map preview for whichever directory it came from if( m_fileExists == FALSE && TheGlobalData) { - if( fileType == FILE_TYPE_TGA ) // just TGA, since we don't dds previews + if( fileType == FILE_TYPE_TGA ) // just TGA, since we don't do dds previews { sprintf(m_filePath,MAP_PREVIEW_DIR_PATH, TheGlobalData->getPath_UserData().str()); //strcpy( m_filePath, USER_TGA_DIR_PATH ); @@ -373,11 +356,9 @@ int GameFileClass::Open(int rights) return(false); } - // just open up the file in m_filePath m_theFile = TheFileSystem->openFile( m_filePath, File::READ | File::BINARY ); return (m_theFile != NULL); - } //------------------------------------------------------------------------------------------------- @@ -448,12 +429,16 @@ void GameFileClass::Close(void) extern W3DFileSystem *TheW3DFileSystem = NULL; //------------------------------------------------------------------------------------------------- -/** Constructor. Creating an instance of this class overrices the default +/** Constructor. Creating an instance of this class overrides the default W3D file factory. */ //------------------------------------------------------------------------------------------------- W3DFileSystem::W3DFileSystem(void) { _TheFileFactory = this; // override the w3d file factory. + +#if RTS_ZEROHOUR && PRIORITIZE_TEXTURES_BY_SIZE + reprioritizeTexturesBySize(); +#endif } //------------------------------------------------------------------------------------------------- @@ -481,3 +466,79 @@ void W3DFileSystem::Return_File( FileClass *file ) delete file; } +//------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------- +void W3DFileSystem::reprioritizeTexturesBySize() +{ + ArchivedDirectoryInfo* dirInfo = TheArchiveFileSystem->friend_getArchivedDirectoryInfo(TGA_DIR_PATH); + if (dirInfo != NULL) + { + reprioritizeTexturesBySize(*dirInfo); + } +} + +//------------------------------------------------------------------------------------------------- +// TheSuperHackers @info This function moves the largest texture of its name to the front of the +// directory info. The algorithm only prioritizes the first item in the multimap, because this is +// what we currently need: +// Before: A(256kb) B(128kb) C(512kb) +// After: C(512kb) B(128kb) A(256kb) +// +// Catered to specific game archives only. This ensures that user created archives are not included +// for the re-prioritization of textures. +//------------------------------------------------------------------------------------------------- +void W3DFileSystem::reprioritizeTexturesBySize(ArchivedDirectoryInfo& dirInfo) +{ + const char* const superiorArchive = "Textures.big"; + const char* const inferiorArchive = "TexturesZH.big"; + + ArchivedFileLocationMap::iterator it0; + ArchivedFileLocationMap::iterator it1 = dirInfo.m_files.begin(); + ArchivedFileLocationMap::iterator end = dirInfo.m_files.end(); + + if (it1 != end) + { + it0 = it1; + ++it1; + } + + for (; it1 != end; ++it1) + { + const AsciiString& file0 = it0->first; + const AsciiString& file1 = it1->first; + + if (file0 == file1) + { + GameFileType type = getFileType(file0.str()); + if (isImageFileType(type)) + { + ArchiveFile* archive0 = it0->second; + ArchiveFile* archive1 = it1->second; + FileInfo info0; + FileInfo info1; + AsciiString filepath(dirInfo.m_path); + filepath.concat(file0); + + if (archive0->getFileInfo(filepath, &info0) && archive1->getFileInfo(filepath, &info1)) + { + if (info0.size() < info1.size() + && archive0->getName().endsWithNoCase(inferiorArchive) + && archive1->getName().endsWithNoCase(superiorArchive)) + { + std::swap(it0->second, it1->second); + +#if ENABLE_FILESYSTEM_LOGGING + DEBUG_LOG(("W3DFileSystem::reprioritizeTexturesBySize - prioritize %s(%ukb) from %s over %s(%ukb) from %s", + file1.str(), UnsignedInt(info1.size() / 1024), archive1->getName().str(), + file0.str(), UnsignedInt(info0.size() / 1024), archive0->getName().str())); +#endif + } + } + } + } + else + { + it0 = it1; + } + } +}