diff --git a/Generals/Code/GameEngine/Include/GameClient/Image.h b/Generals/Code/GameEngine/Include/GameClient/Image.h index 71f9089a46..c4e0876cb5 100644 --- a/Generals/Code/GameEngine/Include/GameClient/Image.h +++ b/Generals/Code/GameEngine/Include/GameClient/Image.h @@ -33,6 +33,7 @@ #include "Common/AsciiString.h" #include "Common/GameMemory.h" #include "Common/SubsystemInterface.h" +#include struct FieldParse; class INI; @@ -106,8 +107,6 @@ friend class ImageCollection; void *m_rawTextureData; ///< raw texture data UnsignedInt m_status; ///< status bits from ImageStatus - Image *m_next; ///< for maintaining lists as collections - static const FieldParse m_imageFieldParseTable[]; ///< the parse table for INI definition }; @@ -130,17 +129,21 @@ class ImageCollection : public SubsystemInterface void load( Int textureSize ); ///< load images const Image *findImageByName( const AsciiString& name ); ///< find image based on name - const Image *findImageByFilename( const AsciiString& name ); ///< find image based on filename - Image *firstImage( void ); ///< return first image in list - Image *nextImage( Image *image ); ///< return next image + /// adds the given image to the collection, transfers ownership to this object + void addImage(Image *image); - Image *newImage( void ); ///< return a new, linked image + /// enumerates the list of existing images + Image *Enum(unsigned index) + { + for (std::map::iterator i=m_imageMap.begin();i!=m_imageMap.end();++i) + if (!index--) + return i->second; + return NULL; + } protected: - - Image *m_imageList; ///< the image list - + std::map m_imageMap; ///< maps named keys to images }; // INLINING /////////////////////////////////////////////////////////////////////////////////////// @@ -148,7 +151,6 @@ inline void Image::setName( AsciiString name ) { m_name = name; } inline AsciiString Image::getName( void ) const { return m_name; } inline void Image::setFilename( AsciiString name ) { m_filename = name; } inline AsciiString Image::getFilename( void ) const { return m_filename; } -inline Image *ImageCollection::firstImage( void ) { return m_imageList; } inline void Image::setUV( Region2D *uv ) { if( uv ) m_UVCoords = *uv; } inline const Region2D *Image::getUV( void ) const { return &m_UVCoords; } inline void Image::setTextureWidth( Int width ) { m_textureSize.x = width; } diff --git a/Generals/Code/GameEngine/Include/GameClient/MapUtil.h b/Generals/Code/GameEngine/Include/GameClient/MapUtil.h index 866a7039cc..4a55b63804 100644 --- a/Generals/Code/GameEngine/Include/GameClient/MapUtil.h +++ b/Generals/Code/GameEngine/Include/GameClient/MapUtil.h @@ -72,6 +72,7 @@ class MapMetaData { public: UnicodeString m_displayName; + AsciiString m_nameLookupTag; Region3D m_extent; Int m_numPlayers; Bool m_isMultiplayer; @@ -125,6 +126,8 @@ Int populateMapListboxNoReset( GameWindow *listbox, Bool useSystemMaps, Bool isM Bool isValidMap( AsciiString mapName, Bool isMultiplayer ); /// Validate a map Image *getMapPreviewImage( AsciiString mapName ); AsciiString getDefaultMap( Bool isMultiplayer ); /// Find a valid map +AsciiString getDefaultOfficialMap(); +Bool isOfficialMap( AsciiString mapName ); Bool parseMapPreviewChunk(DataChunkInput &file, DataChunkInfo *info, void *userData); void findDrawPositions( Int startX, Int startY, Int width, Int height, Region3D extent, ICoord2D *ul, ICoord2D *lr ); diff --git a/Generals/Code/GameEngine/Source/Common/INI/INIMapCache.cpp b/Generals/Code/GameEngine/Source/Common/INI/INIMapCache.cpp index 85efbc6fee..7b23e4caf1 100644 --- a/Generals/Code/GameEngine/Source/Common/INI/INIMapCache.cpp +++ b/Generals/Code/GameEngine/Source/Common/INI/INIMapCache.cpp @@ -33,6 +33,7 @@ #include "Lib/BaseType.h" #include "Common/INI.h" #include "GameClient/MapUtil.h" +#include "GameClient/GameText.h" #include "GameNetwork/NetworkDefs.h" #include "Common/NameKeyGenerator.h" #include "Common/WellKnownKeys.h" @@ -46,6 +47,7 @@ class MapMetaDataReader Int m_numPlayers; Bool m_isMultiplayer; AsciiString m_asciiDisplayName; + AsciiString m_asciiNameLookupTag; Bool m_isOfficial; WinTimeStamp m_timestamp; @@ -92,6 +94,7 @@ const FieldParse MapMetaDataReader::m_mapFieldParseTable[] = { "timestampLo", INI::parseInt, NULL, offsetof( MapMetaDataReader, m_timestamp.m_lowTimeStamp ) }, { "timestampHi", INI::parseInt, NULL, offsetof( MapMetaDataReader, m_timestamp.m_highTimeStamp ) }, { "displayName", INI::parseAsciiString, NULL, offsetof( MapMetaDataReader, m_asciiDisplayName ) }, + { "nameLookupTag", INI::parseAsciiString, NULL, offsetof( MapMetaDataReader, m_asciiNameLookupTag ) }, { "supplyPosition", parseSupplyPositionCoord3D, NULL, NULL }, { "techPosition", parseTechPositionsCoord3D, NULL, NULL }, @@ -136,7 +139,36 @@ void INI::parseMapCacheDefinition( INI* ini ) md.m_waypoints[TheNameKeyGenerator->keyToName(TheKey_InitialCameraPosition)] = mdr.m_initialCameraPosition; +#if RTS_GENERALS md.m_displayName = QuotedPrintableToUnicodeString(mdr.m_asciiDisplayName); +#else + md.m_nameLookupTag = QuotedPrintableToAsciiString(mdr.m_asciiNameLookupTag); + + if (md.m_nameLookupTag.isEmpty()) + { + // maps without localized name tags + AsciiString tempdisplayname; + tempdisplayname = name.reverseFind('\\') + 1; + md.m_displayName.translate(tempdisplayname); + if (md.m_numPlayers >= 2) + { + UnicodeString extension; + extension.format(L" (%d)", md.m_numPlayers); + md.m_displayName.concat(extension); + } + } + else + { + // official maps with name tags + md.m_displayName = TheGameText->fetch(md.m_nameLookupTag); + if (md.m_numPlayers >= 2) + { + UnicodeString extension; + extension.format(L" (%d)", md.m_numPlayers); + md.m_displayName.concat(extension); + } + } +#endif AsciiString startingCamName; for (Int i=0; inewImage(); + image = newInstance(Image); image->setName( name ); + TheMappedImageCollection->addImage(image); DEBUG_ASSERTCRASH( image, ("parseMappedImage: unable to allocate image for '%s'", name.str()) ); diff --git a/Generals/Code/GameEngine/Source/GameClient/MapUtil.cpp b/Generals/Code/GameEngine/Source/GameClient/MapUtil.cpp index ff604a932f..14b7101f3c 100644 --- a/Generals/Code/GameEngine/Source/GameClient/MapUtil.cpp +++ b/Generals/Code/GameEngine/Source/GameClient/MapUtil.cpp @@ -405,7 +405,12 @@ void MapCache::writeCacheINI( Bool userDir ) fprintf(fp, " extentMin = X:%2.2f Y:%2.2f Z:%2.2f\n", md.m_extent.lo.x, md.m_extent.lo.y, md.m_extent.lo.z); fprintf(fp, " extentMax = X:%2.2f Y:%2.2f Z:%2.2f\n", md.m_extent.hi.x, md.m_extent.hi.y, md.m_extent.hi.z); +// BAD AND NOW UNUSED: the mapcache.ini should not contain localized data... using the lookup tag instead +#if RTS_GENERALS fprintf(fp, " displayName = %s\n", UnicodeStringToQuotedPrintable(md.m_displayName).str()); +#else + fprintf(fp, " nameLookupTag = %s\n", md.m_nameLookupTag.str()); +#endif Coord3D pos; WaypointMap::iterator itw = md.m_waypoints.begin(); @@ -645,6 +650,31 @@ Bool MapCache::addMap( AsciiString dirName, AsciiString fname, FileInfo *fileInf if ((md.m_filesize == filesize) && (md.m_CRC != 0)) { + // Force a lookup so that we don't display the English localization in all builds. + if (md.m_nameLookupTag.isEmpty()) + { + // unofficial maps or maps without names + AsciiString tempdisplayname; + tempdisplayname = fname.reverseFind('\\') + 1; + (*this)[lowerFname].m_displayName.translate(tempdisplayname); + if (md.m_numPlayers >= 2) + { + UnicodeString extension; + extension.format(L" (%d)", md.m_numPlayers); + (*this)[lowerFname].m_displayName.concat(extension); + } + } + else + { + // official maps with name tags + (*this)[lowerFname].m_displayName = TheGameText->fetch(md.m_nameLookupTag); + if (md.m_numPlayers >= 2) + { + UnicodeString extension; + extension.format(L" (%d)", md.m_numPlayers); + (*this)[lowerFname].m_displayName.concat(extension); + } + } // DEBUG_LOG(("MapCache::addMap - found match for map %s", lowerFname.str())); return FALSE; // OK, it checks out. } @@ -676,6 +706,7 @@ Bool MapCache::addMap( AsciiString dirName, AsciiString fname, FileInfo *fileInf Bool exists = false; AsciiString munkee = worldDict.getAsciiString(TheKey_mapName, &exists); + md.m_nameLookupTag = munkee; if (!exists || munkee.isEmpty()) { DEBUG_LOG(("Missing TheKey_mapName!")); @@ -766,6 +797,7 @@ Int populateMapListboxNoReset( GameWindow *listbox, Bool useSystemMaps, Bool isM const Image *easyImage = NULL; const Image *mediumImage = NULL; const Image *brutalImage = NULL; + const Image *maxBrutalImage = NULL; SkirmishBattleHonors *battleHonors = NULL; Int w = 10, h = 10; if (numColumns > 1) @@ -773,6 +805,7 @@ Int populateMapListboxNoReset( GameWindow *listbox, Bool useSystemMaps, Bool isM easyImage = TheMappedImageCollection->findImageByName("Star-Bronze"); mediumImage = TheMappedImageCollection->findImageByName("Star-Silver"); brutalImage = TheMappedImageCollection->findImageByName("Star-Gold"); + maxBrutalImage = TheMappedImageCollection->findImageByName("RedYell_Star"); battleHonors = new SkirmishBattleHonors; w = (brutalImage)?brutalImage->getImageWidth():10; @@ -843,6 +876,16 @@ typedef MapDisplayToFileNameList::iterator MapDisplayToFileNameListIter; } */ +#if RTS_ZEROHOUR + //Patch 1.03 -- Purposely filter out these broken maps that exist in Generals. + if( !asciiMapName.compare( "maps\\armored fury\\armored fury.map" ) || + !asciiMapName.compare( "maps\\scorched earth\\scorched earth.map" ) ) + { + ++tempit; + continue; + } +#endif + DEBUG_ASSERTCRASH(it != TheMapCache->end(), ("Map %s not found in map cache.", tempit->str())); if (it->first.startsWithNoCase(mapDir.str()) && isMultiplayer == it->second.m_isMultiplayer && !it->second.m_displayName.isEmpty()) { @@ -857,8 +900,17 @@ typedef MapDisplayToFileNameList::iterator MapDisplayToFileNameListIter; Int numBrutal = battleHonors->getEnduranceMedal(it->first.str(), SLOT_BRUTAL_AI); if (numBrutal) { - imageItemData = 3; - index = GadgetListBoxAddEntryImage( listbox, brutalImage, index, 0, w, h, TRUE); + int maxBrutalSlots = it->second.m_numPlayers - 1; + if (maxBrutalImage != NULL && numBrutal == maxBrutalSlots) + { + index = GadgetListBoxAddEntryImage( listbox, maxBrutalImage, index, 0, w, h, TRUE); + imageItemData = 4; + } + else + { + index = GadgetListBoxAddEntryImage( listbox, brutalImage, index, 0, w, h, TRUE); + imageItemData = 3; + } } else if (numMedium) { @@ -984,6 +1036,39 @@ AsciiString getDefaultMap( Bool isMultiplayer ) return AsciiString::TheEmptyString; } + +AsciiString getDefaultOfficialMap() +{ + if(!TheMapCache) + return AsciiString::TheEmptyString; + TheMapCache->updateCache(); + + MapCache::iterator it = TheMapCache->begin(); + while (it != TheMapCache->end()) + { + if (it->second.m_isMultiplayer && it->second.m_isOfficial) + { + return it->first; + } + ++it; + } + return AsciiString::TheEmptyString; +} + + +Bool isOfficialMap( AsciiString mapName ) +{ + if(!TheMapCache || mapName.isEmpty()) + return FALSE; + TheMapCache->updateCache(); + mapName.toLower(); + MapCache::iterator it = TheMapCache->find(mapName); + if (it != TheMapCache->end()) + return it->second.m_isOfficial; + return FALSE; +} + + const MapMetaData *MapCache::findMap(AsciiString mapName) { mapName.toLower(); @@ -1104,7 +1189,7 @@ Image *getMapPreviewImage( AsciiString mapName ) if (success) { - image = TheMappedImageCollection->newImage(); + image = newInstance(Image); image->setName(tempName); //image->setFullPath("mission.tga"); image->setFilename(name); @@ -1117,6 +1202,7 @@ Image *getMapPreviewImage( AsciiString mapName ) image->setUV(&uv); image->setTextureHeight(128); image->setTextureWidth(128); + TheMappedImageCollection->addImage(image); } else { diff --git a/Generals/Code/GameEngine/Source/GameClient/System/Image.cpp b/Generals/Code/GameEngine/Source/GameClient/System/Image.cpp index 6a820a4e76..de50031979 100644 --- a/Generals/Code/GameEngine/Source/GameClient/System/Image.cpp +++ b/Generals/Code/GameEngine/Source/GameClient/System/Image.cpp @@ -39,7 +39,7 @@ #include "Common/INI.h" #include "Common/GlobalData.h" #include "GameClient/Image.h" - +#include "Common/NameKeyGenerator.h" // PRIVATE DATA /////////////////////////////////////////////////////////////////////////////////// const FieldParse Image::m_imageFieldParseTable[] = @@ -154,7 +154,6 @@ Image::Image( void ) m_imageSize.y = 0; m_rawTextureData = NULL; m_status = IMAGE_STATUS_NONE; - m_next = NULL; } @@ -199,57 +198,22 @@ UnsignedInt Image::clearStatus( UnsignedInt bit ) //------------------------------------------------------------------------------------------------- ImageCollection::ImageCollection( void ) { - - m_imageList = NULL; - } //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- ImageCollection::~ImageCollection( void ) { - Image *image, *next; - - // delete the images - image = m_imageList; - while( image ) - { - - next = image->m_next; - deleteInstance(image); - image = next; - - } - m_imageList = NULL; - + for (std::map::iterator i=m_imageMap.begin();i!=m_imageMap.end();++i) + deleteInstance(i->second); } //------------------------------------------------------------------------------------------------- /** Return the next image in the collection */ //------------------------------------------------------------------------------------------------- -Image *ImageCollection::nextImage( Image *image ) +void ImageCollection::addImage( Image *image ) { - - if( image ) - return image->m_next; - - return NULL; - -} - -//------------------------------------------------------------------------------------------------- -/** Allocate a new image, tie to the image list and return it */ -//------------------------------------------------------------------------------------------------- -Image *ImageCollection::newImage( void ) -{ - Image *image = newInstance(Image); - - // attach to collection list - image->m_next = m_imageList; - m_imageList = image; - - return image; - + m_imageMap[TheNameKeyGenerator->nameToLowercaseKey(image->getName())]=image; } //------------------------------------------------------------------------------------------------- @@ -257,55 +221,8 @@ Image *ImageCollection::newImage( void ) //------------------------------------------------------------------------------------------------- const Image *ImageCollection::findImageByName( const AsciiString& name ) { - Image *image; - - /** @todo this needs to be more intelligent if this image collection - becomes a real system we use a lot */ - - // search the images - image = m_imageList; - while( image ) - { - - // - // want to do a case insensitive compare here cause image INI files are - // autogenerated from filenames using the image packer tool - // - if( image->getName().compareNoCase( name.str() ) == 0 ) - return image; - image = image->m_next; - - } - - // not found - return NULL; - -} - -//------------------------------------------------------------------------------------------------- -/** Find image given image filename */ -//------------------------------------------------------------------------------------------------- -const Image *ImageCollection::findImageByFilename( const AsciiString& filename ) -{ - Image *image; - - /** @todo this needs to be more intelligent if this image collection - becomes a real system we use a lot */ - - // search the images - image = m_imageList; - while( image ) - { - - if( image->getFilename() == filename ) - return image; - image = image->m_next; - - } - - // not found - return NULL; - + std::map::iterator i=m_imageMap.find(TheNameKeyGenerator->nameToLowercaseKey(name)); + return i==m_imageMap.end()?NULL:i->second; } //------------------------------------------------------------------------------------------------- diff --git a/Generals/Code/Tools/GUIEdit/Source/Properties.cpp b/Generals/Code/Tools/GUIEdit/Source/Properties.cpp index a569f8be50..6cf9dcf002 100644 --- a/Generals/Code/Tools/GUIEdit/Source/Properties.cpp +++ b/Generals/Code/Tools/GUIEdit/Source/Properties.cpp @@ -1176,9 +1176,7 @@ void LoadImageListComboBox( HWND comboBox ) SendMessage( comboBox, CB_RESETCONTENT, 0, 0 ); // load the combo box with string names from the GUI image collection - for( image = TheMappedImageCollection->firstImage(); - image; - image = TheMappedImageCollection->nextImage( image ) ) + for (unsigned index=0;(image=TheMappedImageCollection->Enum(index))!=NULL;index++) { SendMessage( comboBox, CB_ADDSTRING, 0, (LPARAM)image->getName().str() ); diff --git a/GeneralsMD/Code/GameEngine/Source/Common/INI/INIMapCache.cpp b/GeneralsMD/Code/GameEngine/Source/Common/INI/INIMapCache.cpp index a1ed70d6b8..b7c2fef7bd 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/INI/INIMapCache.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/INI/INIMapCache.cpp @@ -139,8 +139,9 @@ void INI::parseMapCacheDefinition( INI* ini ) md.m_waypoints[TheNameKeyGenerator->keyToName(TheKey_InitialCameraPosition)] = mdr.m_initialCameraPosition; -// md.m_displayName = QuotedPrintableToUnicodeString(mdr.m_asciiDisplayName); -// this string is never to be used, but we'll leave it in to allow people with an old mapcache.ini to parse it +#if RTS_GENERALS + md.m_displayName = QuotedPrintableToUnicodeString(mdr.m_asciiDisplayName); +#else md.m_nameLookupTag = QuotedPrintableToAsciiString(mdr.m_asciiNameLookupTag); if (md.m_nameLookupTag.isEmpty()) @@ -167,6 +168,7 @@ void INI::parseMapCacheDefinition( INI* ini ) md.m_displayName.concat(extension); } } +#endif AsciiString startingCamName; for (Int i=0; iend(), ("Map %s not found in map cache.", tempit->str())); if (it->first.startsWithNoCase(mapDir.str()) && isMultiplayer == it->second.m_isMultiplayer && !it->second.m_displayName.isEmpty()) @@ -896,7 +901,7 @@ typedef MapDisplayToFileNameList::iterator MapDisplayToFileNameListIter; if (numBrutal) { int maxBrutalSlots = it->second.m_numPlayers - 1; - if (numBrutal == maxBrutalSlots) + if (maxBrutalImage != NULL && numBrutal == maxBrutalSlots) { index = GadgetListBoxAddEntryImage( listbox, maxBrutalImage, index, 0, w, h, TRUE); imageItemData = 4; @@ -1223,7 +1228,7 @@ Image *getMapPreviewImage( AsciiString mapName ) mapPreviewImage->setStatus(IMAGE_STATUS_RAW_TEXTURE); // allocate our terrain texture TextureClass * texture = new TextureClass( size.x, size.y, - WW3D_FORMAT_X8R8G8B8, TextureClass::MIP_LEVELS_1 ); + WW3D_FORMAT_X8R8G8B8, MIP_LEVELS_1 ); uv.lo.x = 0.0f; uv.lo.y = 1.0f; uv.hi.x = 1.0f;