/ghost/map.cpp

http://ghostcb.googlecode.com/ · C++ · 1044 lines · 721 code · 191 blank · 132 comment · 243 complexity · e76a315888bfb9759d89c62ae2b51c51 MD5 · raw file

  1. /*
  2. Copyright [2008] [Trevor Hogan]
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. CODE PORTED FROM THE ORIGINAL GHOST PROJECT: http://ghost.pwner.org/
  13. */
  14. #include "ghost.h"
  15. #include "util.h"
  16. #include "crc32.h"
  17. #include "sha1.h"
  18. #include "config.h"
  19. #include "map.h"
  20. #define __STORMLIB_SELF__
  21. #include <stormlib/StormLib.h>
  22. #define ROTL(x,n) ((x)<<(n))|((x)>>(32-(n))) // this won't work with signed types
  23. #define ROTR(x,n) ((x)>>(n))|((x)<<(32-(n))) // this won't work with signed types
  24. //
  25. // CMap
  26. //
  27. CMap :: CMap( CGHost *nGHost ) : m_GHost( nGHost ), m_Valid( true ), m_MapPath( "Maps\\FrozenThrone\\(12)EmeraldGardens.w3x" ), m_MapSpeed( MAPSPEED_FAST ), m_MapVisibility( MAPVIS_DEFAULT ), m_MapObservers( MAPOBS_NONE ), m_MapFlags( MAPFLAG_TEAMSTOGETHER | MAPFLAG_FIXEDTEAMS ), m_MapFilterMaker( MAPFILTER_MAKER_BLIZZARD ), m_MapFilterType( MAPFILTER_TYPE_MELEE ), m_MapFilterSize( MAPFILTER_SIZE_LARGE ), m_MapFilterObs( MAPFILTER_OBS_NONE ), m_MapOptions( MAPOPT_MELEE ), m_MapLoadInGame( false ), m_MapNumPlayers( 12 ), m_MapNumTeams( 12 )
  28. {
  29. CONSOLE_Print( "[MAP] using hardcoded Emerald Gardens map data for Warcraft 3 version 1.24 & 1.24b" );
  30. m_MapSize = UTIL_ExtractNumbers( "174 221 4 0", 4 );
  31. m_MapInfo = UTIL_ExtractNumbers( "251 57 68 98", 4 );
  32. m_MapCRC = UTIL_ExtractNumbers( "108 250 204 59", 4 );
  33. m_MapSHA1 = UTIL_ExtractNumbers( "35 81 104 182 223 63 204 215 1 17 87 234 220 66 3 185 82 99 6 13", 20 );
  34. m_MapWidth = UTIL_ExtractNumbers( "172 0", 2 );
  35. m_MapHeight = UTIL_ExtractNumbers( "172 0", 2 );
  36. m_Slots.push_back( CGameSlot( 0, 255, SLOTSTATUS_OPEN, 0, 0, 0, SLOTRACE_RANDOM | SLOTRACE_SELECTABLE ) );
  37. m_Slots.push_back( CGameSlot( 0, 255, SLOTSTATUS_OPEN, 0, 1, 1, SLOTRACE_RANDOM | SLOTRACE_SELECTABLE ) );
  38. m_Slots.push_back( CGameSlot( 0, 255, SLOTSTATUS_OPEN, 0, 2, 2, SLOTRACE_RANDOM | SLOTRACE_SELECTABLE ) );
  39. m_Slots.push_back( CGameSlot( 0, 255, SLOTSTATUS_OPEN, 0, 3, 3, SLOTRACE_RANDOM | SLOTRACE_SELECTABLE ) );
  40. m_Slots.push_back( CGameSlot( 0, 255, SLOTSTATUS_OPEN, 0, 4, 4, SLOTRACE_RANDOM | SLOTRACE_SELECTABLE ) );
  41. m_Slots.push_back( CGameSlot( 0, 255, SLOTSTATUS_OPEN, 0, 5, 5, SLOTRACE_RANDOM | SLOTRACE_SELECTABLE ) );
  42. m_Slots.push_back( CGameSlot( 0, 255, SLOTSTATUS_OPEN, 0, 6, 6, SLOTRACE_RANDOM | SLOTRACE_SELECTABLE ) );
  43. m_Slots.push_back( CGameSlot( 0, 255, SLOTSTATUS_OPEN, 0, 7, 7, SLOTRACE_RANDOM | SLOTRACE_SELECTABLE ) );
  44. m_Slots.push_back( CGameSlot( 0, 255, SLOTSTATUS_OPEN, 0, 8, 8, SLOTRACE_RANDOM | SLOTRACE_SELECTABLE ) );
  45. m_Slots.push_back( CGameSlot( 0, 255, SLOTSTATUS_OPEN, 0, 9, 9, SLOTRACE_RANDOM | SLOTRACE_SELECTABLE ) );
  46. m_Slots.push_back( CGameSlot( 0, 255, SLOTSTATUS_OPEN, 0, 10, 10, SLOTRACE_RANDOM | SLOTRACE_SELECTABLE ) );
  47. m_Slots.push_back( CGameSlot( 0, 255, SLOTSTATUS_OPEN, 0, 11, 11, SLOTRACE_RANDOM | SLOTRACE_SELECTABLE ) );
  48. }
  49. CMap :: CMap( CGHost *nGHost, CConfig *CFG, string nCFGFile ) : m_GHost( nGHost )
  50. {
  51. Load( CFG, nCFGFile );
  52. }
  53. CMap :: ~CMap( )
  54. {
  55. }
  56. BYTEARRAY CMap :: GetMapGameFlags( )
  57. {
  58. /*
  59. Speed: (mask 0x00000003) cannot be combined
  60. 0x00000000 - Slow game speed
  61. 0x00000001 - Normal game speed
  62. 0x00000002 - Fast game speed
  63. Visibility: (mask 0x00000F00) cannot be combined
  64. 0x00000100 - Hide terrain
  65. 0x00000200 - Map explored
  66. 0x00000400 - Always visible (no fog of war)
  67. 0x00000800 - Default
  68. Observers/Referees: (mask 0x40003000) cannot be combined
  69. 0x00000000 - No Observers
  70. 0x00002000 - Observers on Defeat
  71. 0x00003000 - Additional players as observer allowed
  72. 0x40000000 - Referees
  73. Teams/Units/Hero/Race: (mask 0x07064000) can be combined
  74. 0x00004000 - Teams Together (team members are placed at neighbored starting locations)
  75. 0x00060000 - Fixed teams
  76. 0x01000000 - Unit share
  77. 0x02000000 - Random hero
  78. 0x04000000 - Random races
  79. */
  80. uint32_t GameFlags = 0;
  81. // speed
  82. if( m_MapSpeed == MAPSPEED_SLOW )
  83. GameFlags = 0x00000000;
  84. else if( m_MapSpeed == MAPSPEED_NORMAL )
  85. GameFlags = 0x00000001;
  86. else
  87. GameFlags = 0x00000002;
  88. // visibility
  89. if( m_MapVisibility == MAPVIS_HIDETERRAIN )
  90. GameFlags |= 0x00000100;
  91. else if( m_MapVisibility == MAPVIS_EXPLORED )
  92. GameFlags |= 0x00000200;
  93. else if( m_MapVisibility == MAPVIS_ALWAYSVISIBLE )
  94. GameFlags |= 0x00000400;
  95. else
  96. GameFlags |= 0x00000800;
  97. // observers
  98. if( m_MapObservers == MAPOBS_ONDEFEAT )
  99. GameFlags |= 0x00002000;
  100. else if( m_MapObservers == MAPOBS_ALLOWED )
  101. GameFlags |= 0x00003000;
  102. else if( m_MapObservers == MAPOBS_REFEREES )
  103. GameFlags |= 0x40000000;
  104. // teams/units/hero/race
  105. if( m_MapFlags & MAPFLAG_TEAMSTOGETHER )
  106. GameFlags |= 0x00004000;
  107. if( m_MapFlags & MAPFLAG_FIXEDTEAMS )
  108. GameFlags |= 0x00060000;
  109. if( m_MapFlags & MAPFLAG_UNITSHARE )
  110. GameFlags |= 0x01000000;
  111. if( m_MapFlags & MAPFLAG_RANDOMHERO )
  112. GameFlags |= 0x02000000;
  113. if( m_MapFlags & MAPFLAG_RANDOMRACES )
  114. GameFlags |= 0x04000000;
  115. return UTIL_CreateByteArray( GameFlags, false );
  116. }
  117. uint32_t CMap :: GetMapGameType( )
  118. {
  119. /* spec stolen from Strilanc as follows:
  120. Public Enum GameTypes As UInteger
  121. None = 0
  122. Unknown0 = 1 << 0 '[always seems to be set?]
  123. '''<summary>Setting this bit causes wc3 to check the map and disc if it is not signed by Blizzard</summary>
  124. AuthenticatedMakerBlizzard = 1 << 3
  125. OfficialMeleeGame = 1 << 5
  126. SavedGame = 1 << 9
  127. PrivateGame = 1 << 11
  128. MakerUser = 1 << 13
  129. MakerBlizzard = 1 << 14
  130. TypeMelee = 1 << 15
  131. TypeScenario = 1 << 16
  132. SizeSmall = 1 << 17
  133. SizeMedium = 1 << 18
  134. SizeLarge = 1 << 19
  135. ObsFull = 1 << 20
  136. ObsOnDeath = 1 << 21
  137. ObsNone = 1 << 22
  138. MaskObs = ObsFull Or ObsOnDeath Or ObsNone
  139. MaskMaker = MakerBlizzard Or MakerUser
  140. MaskType = TypeMelee Or TypeScenario
  141. MaskSize = SizeLarge Or SizeMedium Or SizeSmall
  142. MaskFilterable = MaskObs Or MaskMaker Or MaskType Or MaskSize
  143. End Enum
  144. */
  145. // note: we allow "conflicting" flags to be set at the same time (who knows if this is a good idea)
  146. // we also don't set any flags this class is unaware of such as Unknown0, SavedGame, and PrivateGame
  147. uint32_t GameType = 0;
  148. // maker
  149. if( m_MapFilterMaker & MAPFILTER_MAKER_USER )
  150. GameType |= MAPGAMETYPE_MAKERUSER;
  151. if( m_MapFilterMaker & MAPFILTER_MAKER_BLIZZARD )
  152. GameType |= MAPGAMETYPE_MAKERBLIZZARD;
  153. // type
  154. if( m_MapFilterType & MAPFILTER_TYPE_MELEE )
  155. GameType |= MAPGAMETYPE_TYPEMELEE;
  156. if( m_MapFilterType & MAPFILTER_TYPE_SCENARIO )
  157. GameType |= MAPGAMETYPE_TYPESCENARIO;
  158. // size
  159. if( m_MapFilterSize & MAPFILTER_SIZE_SMALL )
  160. GameType |= MAPGAMETYPE_SIZESMALL;
  161. if( m_MapFilterSize & MAPFILTER_SIZE_MEDIUM )
  162. GameType |= MAPGAMETYPE_SIZEMEDIUM;
  163. if( m_MapFilterSize & MAPFILTER_SIZE_LARGE )
  164. GameType |= MAPGAMETYPE_SIZELARGE;
  165. // obs
  166. if( m_MapFilterObs & MAPFILTER_OBS_FULL )
  167. GameType |= MAPGAMETYPE_OBSFULL;
  168. if( m_MapFilterObs & MAPFILTER_OBS_ONDEATH )
  169. GameType |= MAPGAMETYPE_OBSONDEATH;
  170. if( m_MapFilterObs & MAPFILTER_OBS_NONE )
  171. GameType |= MAPGAMETYPE_OBSNONE;
  172. return GameType;
  173. }
  174. unsigned char CMap :: GetMapLayoutStyle( )
  175. {
  176. // 0 = melee
  177. // 1 = custom forces
  178. // 2 = fixed player settings (not possible with the Warcraft III map editor)
  179. // 3 = custom forces + fixed player settings
  180. if( !( m_MapOptions & MAPOPT_CUSTOMFORCES ) )
  181. return 0;
  182. if( !( m_MapOptions & MAPOPT_FIXEDPLAYERSETTINGS ) )
  183. return 1;
  184. return 3;
  185. }
  186. void CMap :: Load( CConfig *CFG, string nCFGFile )
  187. {
  188. m_Valid = true;
  189. m_CFGFile = nCFGFile;
  190. // load the map data
  191. m_MapLocalPath = CFG->GetString( "map_localpath", string( ) );
  192. m_MapData.clear( );
  193. if( !m_MapLocalPath.empty( ) )
  194. m_MapData = UTIL_FileRead( m_GHost->m_MapPath + m_MapLocalPath );
  195. // load the map MPQ
  196. string MapMPQFileName = m_GHost->m_MapPath + m_MapLocalPath;
  197. HANDLE MapMPQ;
  198. bool MapMPQReady = false;
  199. if( SFileOpenArchive( MapMPQFileName.c_str( ), 0, MPQ_OPEN_FORCE_MPQ_V1, &MapMPQ ) )
  200. {
  201. CONSOLE_Print( "[MAP] loading MPQ file [" + MapMPQFileName + "]" );
  202. MapMPQReady = true;
  203. }
  204. else
  205. CONSOLE_Print( "[MAP] warning - unable to load MPQ file [" + MapMPQFileName + "]" );
  206. // try to calculate map_size, map_info, map_crc, map_sha1
  207. BYTEARRAY MapSize;
  208. BYTEARRAY MapInfo;
  209. BYTEARRAY MapCRC;
  210. BYTEARRAY MapSHA1;
  211. if( !m_MapData.empty( ) )
  212. {
  213. m_GHost->m_SHA->Reset( );
  214. // calculate map_size
  215. MapSize = UTIL_CreateByteArray( (uint32_t)m_MapData.size( ), false );
  216. CONSOLE_Print( "[MAP] calculated map_size = " + UTIL_ByteArrayToDecString( MapSize ) );
  217. // calculate map_info (this is actually the CRC)
  218. MapInfo = UTIL_CreateByteArray( (uint32_t)m_GHost->m_CRC->FullCRC( (unsigned char *)m_MapData.c_str( ), m_MapData.size( ) ), false );
  219. CONSOLE_Print( "[MAP] calculated map_info = " + UTIL_ByteArrayToDecString( MapInfo ) );
  220. // calculate map_crc (this is not the CRC) and map_sha1
  221. // a big thank you to Strilanc for figuring the map_crc algorithm out
  222. string CommonJ = UTIL_FileRead( m_GHost->m_MapCFGPath + "common.j" );
  223. if( CommonJ.empty( ) )
  224. CONSOLE_Print( "[MAP] unable to calculate map_crc/sha1 - unable to read file [" + m_GHost->m_MapCFGPath + "common.j]" );
  225. else
  226. {
  227. string BlizzardJ = UTIL_FileRead( m_GHost->m_MapCFGPath + "blizzard.j" );
  228. if( BlizzardJ.empty( ) )
  229. CONSOLE_Print( "[MAP] unable to calculate map_crc/sha1 - unable to read file [" + m_GHost->m_MapCFGPath + "blizzard.j]" );
  230. else
  231. {
  232. uint32_t Val = 0;
  233. // update: it's possible for maps to include their own copies of common.j and/or blizzard.j
  234. // this code now overrides the default copies if required
  235. bool OverrodeCommonJ = false;
  236. bool OverrodeBlizzardJ = false;
  237. if( MapMPQReady )
  238. {
  239. HANDLE SubFile;
  240. // override common.j
  241. if( SFileOpenFileEx( MapMPQ, "Scripts\\common.j", 0, &SubFile ) )
  242. {
  243. uint32_t FileLength = SFileGetFileSize( SubFile, NULL );
  244. if( FileLength > 0 && FileLength != 0xFFFFFFFF )
  245. {
  246. char *SubFileData = new char[FileLength];
  247. DWORD BytesRead = 0;
  248. if( SFileReadFile( SubFile, SubFileData, FileLength, &BytesRead ) )
  249. {
  250. CONSOLE_Print( "[MAP] overriding default common.j with map copy while calculating map_crc/sha1" );
  251. OverrodeCommonJ = true;
  252. Val = Val ^ XORRotateLeft( (unsigned char *)SubFileData, BytesRead );
  253. m_GHost->m_SHA->Update( (unsigned char *)SubFileData, BytesRead );
  254. }
  255. delete [] SubFileData;
  256. }
  257. SFileCloseFile( SubFile );
  258. }
  259. }
  260. if( !OverrodeCommonJ )
  261. {
  262. Val = Val ^ XORRotateLeft( (unsigned char *)CommonJ.c_str( ), CommonJ.size( ) );
  263. m_GHost->m_SHA->Update( (unsigned char *)CommonJ.c_str( ), CommonJ.size( ) );
  264. }
  265. if( MapMPQReady )
  266. {
  267. HANDLE SubFile;
  268. // override blizzard.j
  269. if( SFileOpenFileEx( MapMPQ, "Scripts\\blizzard.j", 0, &SubFile ) )
  270. {
  271. uint32_t FileLength = SFileGetFileSize( SubFile, NULL );
  272. if( FileLength > 0 && FileLength != 0xFFFFFFFF )
  273. {
  274. char *SubFileData = new char[FileLength];
  275. DWORD BytesRead = 0;
  276. if( SFileReadFile( SubFile, SubFileData, FileLength, &BytesRead ) )
  277. {
  278. CONSOLE_Print( "[MAP] overriding default blizzard.j with map copy while calculating map_crc/sha1" );
  279. OverrodeBlizzardJ = true;
  280. Val = Val ^ XORRotateLeft( (unsigned char *)SubFileData, BytesRead );
  281. m_GHost->m_SHA->Update( (unsigned char *)SubFileData, BytesRead );
  282. }
  283. delete [] SubFileData;
  284. }
  285. SFileCloseFile( SubFile );
  286. }
  287. }
  288. if( !OverrodeBlizzardJ )
  289. {
  290. Val = Val ^ XORRotateLeft( (unsigned char *)BlizzardJ.c_str( ), BlizzardJ.size( ) );
  291. m_GHost->m_SHA->Update( (unsigned char *)BlizzardJ.c_str( ), BlizzardJ.size( ) );
  292. }
  293. Val = ROTL( Val, 3 );
  294. Val = ROTL( Val ^ 0x03F1379E, 3 );
  295. m_GHost->m_SHA->Update( (unsigned char *)"\x9E\x37\xF1\x03", 4 );
  296. if( MapMPQReady )
  297. {
  298. vector<string> FileList;
  299. FileList.push_back( "war3map.j" );
  300. FileList.push_back( "scripts\\war3map.j" );
  301. FileList.push_back( "war3map.w3e" );
  302. FileList.push_back( "war3map.wpm" );
  303. FileList.push_back( "war3map.doo" );
  304. FileList.push_back( "war3map.w3u" );
  305. FileList.push_back( "war3map.w3b" );
  306. FileList.push_back( "war3map.w3d" );
  307. FileList.push_back( "war3map.w3a" );
  308. FileList.push_back( "war3map.w3q" );
  309. bool FoundScript = false;
  310. for( vector<string> :: iterator i = FileList.begin( ); i != FileList.end( ); ++i )
  311. {
  312. // don't use scripts\war3map.j if we've already used war3map.j (yes, some maps have both but only war3map.j is used)
  313. if( FoundScript && *i == "scripts\\war3map.j" )
  314. continue;
  315. HANDLE SubFile;
  316. if( SFileOpenFileEx( MapMPQ, (*i).c_str( ), 0, &SubFile ) )
  317. {
  318. uint32_t FileLength = SFileGetFileSize( SubFile, NULL );
  319. if( FileLength > 0 && FileLength != 0xFFFFFFFF )
  320. {
  321. char *SubFileData = new char[FileLength];
  322. DWORD BytesRead = 0;
  323. if( SFileReadFile( SubFile, SubFileData, FileLength, &BytesRead ) )
  324. {
  325. if( *i == "war3map.j" || *i == "scripts\\war3map.j" )
  326. FoundScript = true;
  327. Val = ROTL( Val ^ XORRotateLeft( (unsigned char *)SubFileData, BytesRead ), 3 );
  328. m_GHost->m_SHA->Update( (unsigned char *)SubFileData, BytesRead );
  329. // DEBUG_Print( "*** found: " + *i );
  330. }
  331. delete [] SubFileData;
  332. }
  333. SFileCloseFile( SubFile );
  334. }
  335. else
  336. {
  337. // DEBUG_Print( "*** not found: " + *i );
  338. }
  339. }
  340. if( !FoundScript )
  341. CONSOLE_Print( "[MAP] couldn't find war3map.j or scripts\\war3map.j in MPQ file, calculated map_crc/sha1 is probably wrong" );
  342. MapCRC = UTIL_CreateByteArray( Val, false );
  343. CONSOLE_Print( "[MAP] calculated map_crc = " + UTIL_ByteArrayToDecString( MapCRC ) );
  344. m_GHost->m_SHA->Final( );
  345. unsigned char SHA1[20];
  346. memset( SHA1, 0, sizeof( unsigned char ) * 20 );
  347. m_GHost->m_SHA->GetHash( SHA1 );
  348. MapSHA1 = UTIL_CreateByteArray( SHA1, 20 );
  349. CONSOLE_Print( "[MAP] calculated map_sha1 = " + UTIL_ByteArrayToDecString( MapSHA1 ) );
  350. }
  351. else
  352. CONSOLE_Print( "[MAP] unable to calculate map_crc/sha1 - map MPQ file not loaded" );
  353. }
  354. }
  355. }
  356. else
  357. CONSOLE_Print( "[MAP] no map data available, using config file for map_size, map_info, map_crc, map_sha1" );
  358. // try to calculate map_width, map_height, map_slot<x>, map_numplayers, map_numteams
  359. uint32_t MapOptions = 0;
  360. BYTEARRAY MapWidth;
  361. BYTEARRAY MapHeight;
  362. uint32_t MapNumPlayers = 0;
  363. uint32_t MapNumTeams = 0;
  364. vector<CGameSlot> Slots;
  365. if( !m_MapData.empty( ) )
  366. {
  367. if( MapMPQReady )
  368. {
  369. HANDLE SubFile;
  370. if( SFileOpenFileEx( MapMPQ, "war3map.w3i", 0, &SubFile ) )
  371. {
  372. uint32_t FileLength = SFileGetFileSize( SubFile, NULL );
  373. if( FileLength > 0 && FileLength != 0xFFFFFFFF )
  374. {
  375. char *SubFileData = new char[FileLength];
  376. DWORD BytesRead = 0;
  377. if( SFileReadFile( SubFile, SubFileData, FileLength, &BytesRead ) )
  378. {
  379. istringstream ISS( string( SubFileData, BytesRead ) );
  380. // war3map.w3i format found at http://www.wc3campaigns.net/tools/specs/index.html by Zepir/PitzerMike
  381. string GarbageString;
  382. uint32_t FileFormat;
  383. uint32_t RawMapWidth;
  384. uint32_t RawMapHeight;
  385. uint32_t RawMapFlags;
  386. uint32_t RawMapNumPlayers;
  387. uint32_t RawMapNumTeams;
  388. ISS.read( (char *)&FileFormat, 4 ); // file format (18 = ROC, 25 = TFT)
  389. if( FileFormat == 18 || FileFormat == 25 )
  390. {
  391. ISS.seekg( 4, ios :: cur ); // number of saves
  392. ISS.seekg( 4, ios :: cur ); // editor version
  393. getline( ISS, GarbageString, '\0' ); // map name
  394. getline( ISS, GarbageString, '\0' ); // map author
  395. getline( ISS, GarbageString, '\0' ); // map description
  396. getline( ISS, GarbageString, '\0' ); // players recommended
  397. ISS.seekg( 32, ios :: cur ); // camera bounds
  398. ISS.seekg( 16, ios :: cur ); // camera bounds complements
  399. ISS.read( (char *)&RawMapWidth, 4 ); // map width
  400. ISS.read( (char *)&RawMapHeight, 4 ); // map height
  401. ISS.read( (char *)&RawMapFlags, 4 ); // flags
  402. ISS.seekg( 1, ios :: cur ); // map main ground type
  403. if( FileFormat == 18 )
  404. ISS.seekg( 4, ios :: cur ); // campaign background number
  405. else if( FileFormat == 25 )
  406. {
  407. ISS.seekg( 4, ios :: cur ); // loading screen background number
  408. getline( ISS, GarbageString, '\0' ); // path of custom loading screen model
  409. }
  410. getline( ISS, GarbageString, '\0' ); // map loading screen text
  411. getline( ISS, GarbageString, '\0' ); // map loading screen title
  412. getline( ISS, GarbageString, '\0' ); // map loading screen subtitle
  413. if( FileFormat == 18 )
  414. ISS.seekg( 4, ios :: cur ); // map loading screen number
  415. else if( FileFormat == 25 )
  416. {
  417. ISS.seekg( 4, ios :: cur ); // used game data set
  418. getline( ISS, GarbageString, '\0' ); // prologue screen path
  419. }
  420. getline( ISS, GarbageString, '\0' ); // prologue screen text
  421. getline( ISS, GarbageString, '\0' ); // prologue screen title
  422. getline( ISS, GarbageString, '\0' ); // prologue screen subtitle
  423. if( FileFormat == 25 )
  424. {
  425. ISS.seekg( 4, ios :: cur ); // uses terrain fog
  426. ISS.seekg( 4, ios :: cur ); // fog start z height
  427. ISS.seekg( 4, ios :: cur ); // fog end z height
  428. ISS.seekg( 4, ios :: cur ); // fog density
  429. ISS.seekg( 1, ios :: cur ); // fog red value
  430. ISS.seekg( 1, ios :: cur ); // fog green value
  431. ISS.seekg( 1, ios :: cur ); // fog blue value
  432. ISS.seekg( 1, ios :: cur ); // fog alpha value
  433. ISS.seekg( 4, ios :: cur ); // global weather id
  434. getline( ISS, GarbageString, '\0' ); // custom sound environment
  435. ISS.seekg( 1, ios :: cur ); // tileset id of the used custom light environment
  436. ISS.seekg( 1, ios :: cur ); // custom water tinting red value
  437. ISS.seekg( 1, ios :: cur ); // custom water tinting green value
  438. ISS.seekg( 1, ios :: cur ); // custom water tinting blue value
  439. ISS.seekg( 1, ios :: cur ); // custom water tinting alpha value
  440. }
  441. ISS.read( (char *)&RawMapNumPlayers, 4 ); // number of players
  442. uint32_t ClosedSlots = 0;
  443. for( uint32_t i = 0; i < RawMapNumPlayers; ++i )
  444. {
  445. CGameSlot Slot( 0, 255, SLOTSTATUS_OPEN, 0, 0, 1, SLOTRACE_RANDOM );
  446. uint32_t Colour;
  447. uint32_t Status;
  448. uint32_t Race;
  449. ISS.read( (char *)&Colour, 4 ); // colour
  450. Slot.SetColour( Colour );
  451. ISS.read( (char *)&Status, 4 ); // status
  452. if( Status == 1 )
  453. Slot.SetSlotStatus( SLOTSTATUS_OPEN );
  454. else if( Status == 2 )
  455. {
  456. Slot.SetSlotStatus( SLOTSTATUS_OCCUPIED );
  457. Slot.SetComputer( 1 );
  458. Slot.SetComputerType( SLOTCOMP_NORMAL );
  459. }
  460. else
  461. {
  462. Slot.SetSlotStatus( SLOTSTATUS_CLOSED );
  463. ++ClosedSlots;
  464. }
  465. ISS.read( (char *)&Race, 4 ); // race
  466. if( Race == 1 )
  467. Slot.SetRace( SLOTRACE_HUMAN );
  468. else if( Race == 2 )
  469. Slot.SetRace( SLOTRACE_ORC );
  470. else if( Race == 3 )
  471. Slot.SetRace( SLOTRACE_UNDEAD );
  472. else if( Race == 4 )
  473. Slot.SetRace( SLOTRACE_NIGHTELF );
  474. else
  475. Slot.SetRace( SLOTRACE_RANDOM );
  476. ISS.seekg( 4, ios :: cur ); // fixed start position
  477. getline( ISS, GarbageString, '\0' ); // player name
  478. ISS.seekg( 4, ios :: cur ); // start position x
  479. ISS.seekg( 4, ios :: cur ); // start position y
  480. ISS.seekg( 4, ios :: cur ); // ally low priorities
  481. ISS.seekg( 4, ios :: cur ); // ally high priorities
  482. if( Slot.GetSlotStatus( ) != SLOTSTATUS_CLOSED )
  483. Slots.push_back( Slot );
  484. }
  485. ISS.read( (char *)&RawMapNumTeams, 4 ); // number of teams
  486. for( uint32_t i = 0; i < RawMapNumTeams; ++i )
  487. {
  488. uint32_t Flags;
  489. uint32_t PlayerMask;
  490. ISS.read( (char *)&Flags, 4 ); // flags
  491. ISS.read( (char *)&PlayerMask, 4 ); // player mask
  492. for( unsigned char j = 0; j < 12; ++j )
  493. {
  494. if( PlayerMask & 1 )
  495. {
  496. for( vector<CGameSlot> :: iterator k = Slots.begin( ); k != Slots.end( ); ++k )
  497. {
  498. if( (*k).GetColour( ) == j )
  499. (*k).SetTeam( i );
  500. }
  501. }
  502. PlayerMask >>= 1;
  503. }
  504. getline( ISS, GarbageString, '\0' ); // team name
  505. }
  506. // the bot only cares about the following options: melee, fixed player settings, custom forces
  507. // let's not confuse the user by displaying erroneous map options so zero them out now
  508. MapOptions = RawMapFlags & ( MAPOPT_MELEE | MAPOPT_FIXEDPLAYERSETTINGS | MAPOPT_CUSTOMFORCES );
  509. CONSOLE_Print( "[MAP] calculated map_options = " + UTIL_ToString( MapOptions ) );
  510. MapWidth = UTIL_CreateByteArray( (uint16_t)RawMapWidth, false );
  511. CONSOLE_Print( "[MAP] calculated map_width = " + UTIL_ByteArrayToDecString( MapWidth ) );
  512. MapHeight = UTIL_CreateByteArray( (uint16_t)RawMapHeight, false );
  513. CONSOLE_Print( "[MAP] calculated map_height = " + UTIL_ByteArrayToDecString( MapHeight ) );
  514. MapNumPlayers = RawMapNumPlayers - ClosedSlots;
  515. CONSOLE_Print( "[MAP] calculated map_numplayers = " + UTIL_ToString( MapNumPlayers ) );
  516. MapNumTeams = RawMapNumTeams;
  517. CONSOLE_Print( "[MAP] calculated map_numteams = " + UTIL_ToString( MapNumTeams ) );
  518. uint32_t SlotNum = 1;
  519. for( vector<CGameSlot> :: iterator i = Slots.begin( ); i != Slots.end( ); ++i )
  520. {
  521. CONSOLE_Print( "[MAP] calculated map_slot" + UTIL_ToString( SlotNum ) + " = " + UTIL_ByteArrayToDecString( (*i).GetByteArray( ) ) );
  522. ++SlotNum;
  523. }
  524. if( MapOptions & MAPOPT_MELEE )
  525. {
  526. CONSOLE_Print( "[MAP] found melee map, initializing slots" );
  527. // give each slot a different team and set the race to random
  528. unsigned char Team = 0;
  529. for( vector<CGameSlot> :: iterator i = Slots.begin( ); i != Slots.end( ); ++i )
  530. {
  531. (*i).SetTeam( Team++ );
  532. (*i).SetRace( SLOTRACE_RANDOM );
  533. }
  534. }
  535. if( !( MapOptions & MAPOPT_FIXEDPLAYERSETTINGS ) )
  536. {
  537. // make races selectable
  538. for( vector<CGameSlot> :: iterator i = Slots.begin( ); i != Slots.end( ); ++i )
  539. (*i).SetRace( (*i).GetRace( ) | SLOTRACE_SELECTABLE );
  540. }
  541. }
  542. }
  543. else
  544. CONSOLE_Print( "[MAP] unable to calculate map_options, map_width, map_height, map_slot<x>, map_numplayers, map_numteams - unable to extract war3map.w3i from MPQ file" );
  545. delete [] SubFileData;
  546. }
  547. SFileCloseFile( SubFile );
  548. }
  549. else
  550. CONSOLE_Print( "[MAP] unable to calculate map_options, map_width, map_height, map_slot<x>, map_numplayers, map_numteams - couldn't find war3map.w3i in MPQ file" );
  551. }
  552. else
  553. CONSOLE_Print( "[MAP] unable to calculate map_options, map_width, map_height, map_slot<x>, map_numplayers, map_numteams - map MPQ file not loaded" );
  554. }
  555. else
  556. CONSOLE_Print( "[MAP] no map data available, using config file for map_options, map_width, map_height, map_slot<x>, map_numplayers, map_numteams" );
  557. // close the map MPQ
  558. if( MapMPQReady )
  559. SFileCloseArchive( MapMPQ );
  560. m_MapPath = CFG->GetString( "map_path", string( ) );
  561. if( MapSize.empty( ) )
  562. MapSize = UTIL_ExtractNumbers( CFG->GetString( "map_size", string( ) ), 4 );
  563. else if( CFG->Exists( "map_size" ) )
  564. {
  565. CONSOLE_Print( "[MAP] overriding calculated map_size with config value map_size = " + CFG->GetString( "map_size", string( ) ) );
  566. MapSize = UTIL_ExtractNumbers( CFG->GetString( "map_size", string( ) ), 4 );
  567. }
  568. m_MapSize = MapSize;
  569. if( MapInfo.empty( ) )
  570. MapInfo = UTIL_ExtractNumbers( CFG->GetString( "map_info", string( ) ), 4 );
  571. else if( CFG->Exists( "map_info" ) )
  572. {
  573. CONSOLE_Print( "[MAP] overriding calculated map_info with config value map_info = " + CFG->GetString( "map_info", string( ) ) );
  574. MapInfo = UTIL_ExtractNumbers( CFG->GetString( "map_info", string( ) ), 4 );
  575. }
  576. m_MapInfo = MapInfo;
  577. if( MapCRC.empty( ) )
  578. MapCRC = UTIL_ExtractNumbers( CFG->GetString( "map_crc", string( ) ), 4 );
  579. else if( CFG->Exists( "map_crc" ) )
  580. {
  581. CONSOLE_Print( "[MAP] overriding calculated map_crc with config value map_crc = " + CFG->GetString( "map_crc", string( ) ) );
  582. MapCRC = UTIL_ExtractNumbers( CFG->GetString( "map_crc", string( ) ), 4 );
  583. }
  584. m_MapCRC = MapCRC;
  585. if( MapSHA1.empty( ) )
  586. MapSHA1 = UTIL_ExtractNumbers( CFG->GetString( "map_sha1", string( ) ), 20 );
  587. else if( CFG->Exists( "map_sha1" ) )
  588. {
  589. CONSOLE_Print( "[MAP] overriding calculated map_sha1 with config value map_sha1 = " + CFG->GetString( "map_sha1", string( ) ) );
  590. MapSHA1 = UTIL_ExtractNumbers( CFG->GetString( "map_sha1", string( ) ), 20 );
  591. }
  592. m_MapSHA1 = MapSHA1;
  593. m_MapSpeed = CFG->GetInt( "map_speed", MAPSPEED_FAST );
  594. m_MapVisibility = CFG->GetInt( "map_visibility", MAPVIS_DEFAULT );
  595. m_MapObservers = CFG->GetInt( "map_observers", MAPOBS_NONE );
  596. m_MapFlags = CFG->GetInt( "map_flags", MAPFLAG_TEAMSTOGETHER | MAPFLAG_FIXEDTEAMS );
  597. m_MapFilterMaker = CFG->GetInt( "map_filter_maker", MAPFILTER_MAKER_USER );
  598. m_MapFilterType = CFG->GetInt( "map_filter_type", 0 );
  599. m_MapFilterSize = CFG->GetInt( "map_filter_size", MAPFILTER_SIZE_LARGE );
  600. m_MapFilterObs = CFG->GetInt( "map_filter_obs", MAPFILTER_OBS_NONE );
  601. // todotodo: it might be possible for MapOptions to legitimately be zero so this is not a valid way of checking if it wasn't parsed out earlier
  602. if( MapOptions == 0 )
  603. MapOptions = CFG->GetInt( "map_options", 0 );
  604. else if( CFG->Exists( "map_options" ) )
  605. {
  606. CONSOLE_Print( "[MAP] overriding calculated map_options with config value map_options = " + CFG->GetString( "map_options", string( ) ) );
  607. MapOptions = CFG->GetInt( "map_options", 0 );
  608. }
  609. m_MapOptions = MapOptions;
  610. if( MapWidth.empty( ) )
  611. MapWidth = UTIL_ExtractNumbers( CFG->GetString( "map_width", string( ) ), 2 );
  612. else if( CFG->Exists( "map_width" ) )
  613. {
  614. CONSOLE_Print( "[MAP] overriding calculated map_width with config value map_width = " + CFG->GetString( "map_width", string( ) ) );
  615. MapWidth = UTIL_ExtractNumbers( CFG->GetString( "map_width", string( ) ), 2 );
  616. }
  617. m_MapWidth = MapWidth;
  618. if( MapHeight.empty( ) )
  619. MapHeight = UTIL_ExtractNumbers( CFG->GetString( "map_height", string( ) ), 2 );
  620. else if( CFG->Exists( "map_height" ) )
  621. {
  622. CONSOLE_Print( "[MAP] overriding calculated map_height with config value map_height = " + CFG->GetString( "map_height", string( ) ) );
  623. MapHeight = UTIL_ExtractNumbers( CFG->GetString( "map_height", string( ) ), 2 );
  624. }
  625. m_MapHeight = MapHeight;
  626. if (m_MapLocalPath.substr(0,4)=="DotA")
  627. {
  628. m_MapType= "dota";
  629. m_MapMatchMakingCategory = "dota_elo";
  630. }
  631. else
  632. {
  633. m_MapType = CFG->GetString( "map_type", string( ) );
  634. m_MapMatchMakingCategory = CFG->GetString( "map_matchmakingcategory", string( ) );
  635. }
  636. m_MapStatsW3MMDCategory = CFG->GetString( "map_statsw3mmdcategory", string( ) );
  637. m_MapDefaultHCL = CFG->GetString( "map_defaulthcl", string( ) );
  638. //ghost custom build additions
  639. // @disturbed_oc
  640. m_HCLCommandFromGameName = CFG->GetInt( "map_hclfromgamename", 0 ) == 0 ? false : true;
  641. if ( m_GHost->m_HCLCommandFromGameName )
  642. m_HCLCommandFromGameName = true;
  643. m_HCLValidModes = CFG->GetString( "map_validmodes", string( ) );
  644. m_MapGameNameWithMode = CFG->GetString( "map_gamenamewithmode", string( ) );
  645. // @end
  646. m_MapDefaultPlayerScore = CFG->GetInt( "map_defaultplayerscore", 1000 );
  647. m_MapLoadInGame = CFG->GetInt( "map_loadingame", 0 ) == 0 ? false : true;
  648. if (m_GHost->m_ForceLoadInGame)
  649. m_MapLoadInGame = true;
  650. if( MapNumPlayers == 0 )
  651. MapNumPlayers = CFG->GetInt( "map_numplayers", 0 );
  652. else if( CFG->Exists( "map_numplayers" ) )
  653. {
  654. CONSOLE_Print( "[MAP] overriding calculated map_numplayers with config value map_numplayers = " + CFG->GetString( "map_numplayers", string( ) ) );
  655. MapNumPlayers = CFG->GetInt( "map_numplayers", 0 );
  656. }
  657. m_MapNumPlayers = MapNumPlayers;
  658. if( MapNumTeams == 0 )
  659. MapNumTeams = CFG->GetInt( "map_numteams", 0 );
  660. else if( CFG->Exists( "map_numteams" ) )
  661. {
  662. CONSOLE_Print( "[MAP] overriding calculated map_numteams with config value map_numteams = " + CFG->GetString( "map_numteams", string( ) ) );
  663. MapNumTeams = CFG->GetInt( "map_numteams", 0 );
  664. }
  665. m_MapNumTeams = MapNumTeams;
  666. if( Slots.empty( ) )
  667. {
  668. for( uint32_t Slot = 1; Slot <= 12; ++Slot )
  669. {
  670. string SlotString = CFG->GetString( "map_slot" + UTIL_ToString( Slot ), string( ) );
  671. if( SlotString.empty( ) )
  672. break;
  673. BYTEARRAY SlotData = UTIL_ExtractNumbers( SlotString, 9 );
  674. Slots.push_back( CGameSlot( SlotData ) );
  675. }
  676. }
  677. else if( CFG->Exists( "map_slot1" ) )
  678. {
  679. CONSOLE_Print( "[MAP] overriding slots" );
  680. Slots.clear( );
  681. for( uint32_t Slot = 1; Slot <= 12; ++Slot )
  682. {
  683. string SlotString = CFG->GetString( "map_slot" + UTIL_ToString( Slot ), string( ) );
  684. if( SlotString.empty( ) )
  685. break;
  686. BYTEARRAY SlotData = UTIL_ExtractNumbers( SlotString, 9 );
  687. Slots.push_back( CGameSlot( SlotData ) );
  688. }
  689. }
  690. m_Slots = Slots;
  691. // if random races is set force every slot's race to random
  692. if( m_MapFlags & MAPFLAG_RANDOMRACES )
  693. {
  694. CONSOLE_Print( "[MAP] forcing races to random" );
  695. for( vector<CGameSlot> :: iterator i = m_Slots.begin( ); i != m_Slots.end( ); ++i )
  696. (*i).SetRace( SLOTRACE_RANDOM );
  697. }
  698. // add observer slots
  699. if( m_MapObservers == MAPOBS_ALLOWED || m_MapObservers == MAPOBS_REFEREES )
  700. {
  701. CONSOLE_Print( "[MAP] adding " + UTIL_ToString( 12 - m_Slots.size( ) ) + " observer slots" );
  702. while( m_Slots.size( ) < 12 )
  703. m_Slots.push_back( CGameSlot( 0, 255, SLOTSTATUS_OPEN, 0, 12, 12, SLOTRACE_RANDOM ) );
  704. }
  705. CheckValid( );
  706. }
  707. // @disturbed_oc
  708. // string CMAP :: GetMapGameNameWithRandomMode()
  709. // Returns a gamename based on cfg value 'map_gamenamewitmode' with a random mode picked from cfg value 'map_validhcl', which is later encoded to hcl string at game start.
  710. // map_gamenamewithmode = DotA $mode$ GHost++
  711. // map_validhcl = -apso -arso -sdso -rdso
  712. // Will result in for example DotA -arso GHost++ and HCL command will be set accordingly.
  713. string CMap::GetMapGameNameWithRandomMode()
  714. {
  715. string GameName;
  716. string find, replace;
  717. string::size_type loc;
  718. vector<string> modes;
  719. if (m_MapGameNameWithMode.empty() || !m_HCLCommandFromGameName || m_HCLValidModes.empty())
  720. return "";
  721. GameName = m_MapGameNameWithMode;
  722. find = "$mode$";
  723. loc = GameName.find( find );
  724. if ( loc != string::npos )
  725. {
  726. modes = UTIL_Tokenize(m_HCLValidModes, ' ');
  727. //CONSOLE_Print( "[MAPMODE] Gamename with random mode [" + GameName + "] Found [" + UTIL_ToString(modes.size()) + "] Valid modes [" + m_HCLValidModes + "]" );
  728. replace = modes[rand() % modes.size()];
  729. //CONSOLE_Print( "[MAPMODE] HCL Command will be [" + replace.substr(1) + "]" );
  730. GameName.replace(loc, find.size(), replace);
  731. return GameName;
  732. }
  733. else // no $mode$ found stick to autohost name
  734. {
  735. m_HCLCommandFromGameName = false;
  736. return "";
  737. }
  738. }
  739. // @end
  740. void CMap :: CheckValid( )
  741. {
  742. // todotodo: should this code fix any errors it sees rather than just warning the user?
  743. if( m_MapPath.empty( ) || m_MapPath.length( ) >= 50 )
  744. {
  745. m_Valid = false;
  746. CONSOLE_Print( "[MAP] invalid map_path detected" );
  747. }
  748. else if( m_MapPath[0] == '\\' )
  749. CONSOLE_Print( "[MAP] warning - map_path starts with '\\', any replays saved by GHost++ will not be playable in Warcraft III" );
  750. if( m_MapPath.find( '/' ) != string :: npos )
  751. CONSOLE_Print( "[MAP] warning - map_path contains forward slashes '/' but it must use Windows style back slashes '\\'" );
  752. if( m_MapSize.size( ) != 4 )
  753. {
  754. m_Valid = false;
  755. CONSOLE_Print( "[MAP] invalid map_size detected" );
  756. }
  757. else if( !m_MapData.empty( ) && m_MapData.size( ) != UTIL_ByteArrayToUInt32( m_MapSize, false ) )
  758. {
  759. m_Valid = false;
  760. CONSOLE_Print( "[MAP] invalid map_size detected - size mismatch with actual map data" );
  761. }
  762. if( m_MapInfo.size( ) != 4 )
  763. {
  764. m_Valid = false;
  765. CONSOLE_Print( "[MAP] invalid map_info detected" );
  766. }
  767. if( m_MapCRC.size( ) != 4 )
  768. {
  769. m_Valid = false;
  770. CONSOLE_Print( "[MAP] invalid map_crc detected" );
  771. }
  772. if( m_MapSHA1.size( ) != 20 )
  773. {
  774. m_Valid = false;
  775. CONSOLE_Print( "[MAP] invalid map_sha1 detected" );
  776. }
  777. if( m_MapSpeed != MAPSPEED_SLOW && m_MapSpeed != MAPSPEED_NORMAL && m_MapSpeed != MAPSPEED_FAST )
  778. {
  779. m_Valid = false;
  780. CONSOLE_Print( "[MAP] invalid map_speed detected" );
  781. }
  782. if( m_MapVisibility != MAPVIS_HIDETERRAIN && m_MapVisibility != MAPVIS_EXPLORED && m_MapVisibility != MAPVIS_ALWAYSVISIBLE && m_MapVisibility != MAPVIS_DEFAULT )
  783. {
  784. m_Valid = false;
  785. CONSOLE_Print( "[MAP] invalid map_visibility detected" );
  786. }
  787. if( m_MapObservers != MAPOBS_NONE && m_MapObservers != MAPOBS_ONDEFEAT && m_MapObservers != MAPOBS_ALLOWED && m_MapObservers != MAPOBS_REFEREES )
  788. {
  789. m_Valid = false;
  790. CONSOLE_Print( "[MAP] invalid map_observers detected" );
  791. }
  792. // todotodo: m_MapFlags
  793. // todotodo: m_MapFilterMaker, m_MapFilterType, m_MapFilterSize, m_MapFilterObs
  794. if( m_MapWidth.size( ) != 2 )
  795. {
  796. m_Valid = false;
  797. CONSOLE_Print( "[MAP] invalid map_width detected" );
  798. }
  799. if( m_MapHeight.size( ) != 2 )
  800. {
  801. m_Valid = false;
  802. CONSOLE_Print( "[MAP] invalid map_height detected" );
  803. }
  804. if( m_MapNumPlayers == 0 || m_MapNumPlayers > 12 )
  805. {
  806. m_Valid = false;
  807. CONSOLE_Print( "[MAP] invalid map_numplayers detected" );
  808. }
  809. if( m_MapNumTeams == 0 || m_MapNumTeams > 12 )
  810. {
  811. m_Valid = false;
  812. CONSOLE_Print( "[MAP] invalid map_numteams detected" );
  813. }
  814. if( m_Slots.empty( ) || m_Slots.size( ) > 12 )
  815. {
  816. m_Valid = false;
  817. CONSOLE_Print( "[MAP] invalid map_slot<x> detected" );
  818. }
  819. }
  820. uint32_t CMap :: XORRotateLeft( unsigned char *data, uint32_t length )
  821. {
  822. // a big thank you to Strilanc for figuring this out
  823. uint32_t i = 0;
  824. uint32_t Val = 0;
  825. if( length > 3 )
  826. {
  827. while( i < length - 3 )
  828. {
  829. Val = ROTL( Val ^ ( (uint32_t)data[i] + (uint32_t)( data[i + 1] << 8 ) + (uint32_t)( data[i + 2] << 16 ) + (uint32_t)( data[i + 3] << 24 ) ), 3 );
  830. i += 4;
  831. }
  832. }
  833. while( i < length )
  834. {
  835. Val = ROTL( Val ^ data[i], 3 );
  836. ++i;
  837. }
  838. return Val;
  839. }