Hi.
I have an issue with some maps for Warcraft III, which as you know, is MPQ archives :)
The issue is that for some map's map_crc and map_sha1 calculated incorrectly, but there are no error reported! I have no idea why. But i am sure it's related to StormLib. Because with StormLib 6.25 it calculated values correctly.
I had few map's in past, but now i can provide only one map. Maybe i can find other ones later, if needed.
Map link: http://mvpro.net/share/github/CAT%20catch%20MOUSE%201.41v.w3x
(please don't ponder on the name, i am not an author of this map)
Here's a values calculated by StormLib 8.0, 9.0 and current Github dev (i am not sure, maybe issue stay also in 6.20-6.24 or something like it):
[MAP] calculated map_crc = 55 253 222 169
[MAP] calculated map_sha1 = 101 122 149 64 214 142 228 32 142 133 223 200 64 19 67 220 189 60 110 126
Full output of application (GHost++):
[MAP] loading MPQ file [../maps/CAT catch MOUSE 1.41v.w3x]
[MAP] calculated map_size = 197 178 21 0
[MAP] calculated map_info = 154 142 2 148
[MAP] calculated map_crc = 55 253 222 169
[MAP] calculated map_sha1 = 101 122 149 64 214 142 228 32 142 133 223 200 64 19 67 220 189 60 110 126
[MAP] calculated map_options = 96
[MAP] calculated map_width = 56 0
[MAP] calculated map_height = 62 0
[MAP] calculated map_numplayers = 12
[MAP] calculated map_numteams = 2
[MAP] calculated map_slot1 = 0 255 0 0 0 0 8 1 100
[MAP] calculated map_slot2 = 0 255 0 0 0 1 8 1 100
[MAP] calculated map_slot3 = 0 255 0 0 0 2 8 1 100
[MAP] calculated map_slot4 = 0 255 0 0 0 3 8 1 100
[MAP] calculated map_slot5 = 0 255 0 0 0 4 8 1 100
[MAP] calculated map_slot6 = 0 255 0 0 0 5 8 1 100
[MAP] calculated map_slot7 = 0 255 0 0 0 6 8 1 100
[MAP] calculated map_slot8 = 0 255 0 0 0 7 8 1 100
[MAP] calculated map_slot9 = 0 255 0 0 1 8 8 1 100
[MAP] calculated map_slot10 = 0 255 0 0 1 9 8 1 100
[MAP] calculated map_slot11 = 0 255 0 0 1 10 8 1 100
[MAP] calculated map_slot12 = 0 255 0 0 1 11 8 1 100
But the correct values is with StormLib 6.25. Nothing changed expect StormLib version:
[MAP] calculated map_crc = 60 51 205 13
[MAP] calculated map_sha1 = 207 81 172 186 6 32 239 51 249 60 101 79 140 41 148 90 90 8 49 176
Full:
[MAP] loading MPQ file [../maps/CAT catch MOUSE 1.41v.w3x]
[MAP] calculated map_size = 197 178 21 0
[MAP] calculated map_info = 154 142 2 148
[MAP] calculated map_crc = 60 51 205 13
[MAP] calculated map_sha1 = 207 81 172 186 6 32 239 51 249 60 101 79 140 41 148 90 90 8 49 176
[MAP] calculated map_options = 96
[MAP] calculated map_width = 56 0
[MAP] calculated map_height = 62 0
[MAP] calculated map_numplayers = 12
[MAP] calculated map_numteams = 2
[MAP] calculated map_slot1 = 0 255 0 0 0 0 8 1 100
[MAP] calculated map_slot2 = 0 255 0 0 0 1 8 1 100
[MAP] calculated map_slot3 = 0 255 0 0 0 2 8 1 100
[MAP] calculated map_slot4 = 0 255 0 0 0 3 8 1 100
[MAP] calculated map_slot5 = 0 255 0 0 0 4 8 1 100
[MAP] calculated map_slot6 = 0 255 0 0 0 5 8 1 100
[MAP] calculated map_slot7 = 0 255 0 0 0 6 8 1 100
[MAP] calculated map_slot8 = 0 255 0 0 0 7 8 1 100
[MAP] calculated map_slot9 = 0 255 0 0 1 8 8 1 100
[MAP] calculated map_slot10 = 0 255 0 0 1 9 8 1 100
[MAP] calculated map_slot11 = 0 255 0 0 1 10 8 1 100
[MAP] calculated map_slot12 = 0 255 0 0 1 11 8 1 100
I understand, is value correct or not, only in game. With the StormLib 6.25 here's no problem. With other version, i tested, issue (can't play), because GHost++ || Game kicks me (player) because of incorrect map_crc / map_sha1 values.
Code in application, which calculates map_crc and map_sha1:
// try to calculate map_size, map_info, map_crc, map_sha1
BYTEARRAY MapSize;
BYTEARRAY MapInfo;
BYTEARRAY MapCRC;
BYTEARRAY MapSHA1;
if( !m_MapData.empty( ) )
{
m_GHost->m_SHA->Reset( );
// calculate map_size
MapSize = UTIL_CreateByteArray( (uint32_t)m_MapData.size( ), false );
CONSOLE_Print( "[MAP] calculated map_size = " + UTIL_ByteArrayToDecString( MapSize ) );
// calculate map_info (this is actually the CRC)
MapInfo = UTIL_CreateByteArray( (uint32_t)m_GHost->m_CRC->FullCRC( (unsigned char *)m_MapData.c_str( ), m_MapData.size( ) ), false );
CONSOLE_Print( "[MAP] calculated map_info = " + UTIL_ByteArrayToDecString( MapInfo ) );
// calculate map_crc (this is not the CRC) and map_sha1
// a big thank you to Strilanc for figuring the map_crc algorithm out
string CommonJ = UTIL_FileRead( m_GHost->m_MapCFGPath + "common.j" );
if( CommonJ.empty( ) )
CONSOLE_Print( "[MAP] unable to calculate map_crc/sha1 - unable to read file [" + m_GHost->m_MapCFGPath + "common.j]" );
else
{
string BlizzardJ = UTIL_FileRead( m_GHost->m_MapCFGPath + "blizzard.j" );
if( BlizzardJ.empty( ) )
CONSOLE_Print( "[MAP] unable to calculate map_crc/sha1 - unable to read file [" + m_GHost->m_MapCFGPath + "blizzard.j]" );
else
{
uint32_t Val = 0;
// update: it's possible for maps to include their own copies of common.j and/or blizzard.j
// this code now overrides the default copies if required
bool OverrodeCommonJ = false;
bool OverrodeBlizzardJ = false;
if( MapMPQReady )
{
HANDLE SubFile;
// override common.j
if( SFileOpenFileEx( MapMPQ, "Scripts\\common.j", 0, &SubFile ) )
{
uint32_t FileLength = SFileGetFileSize( SubFile, NULL );
if( FileLength > 0 && FileLength != 0xFFFFFFFF )
{
char *SubFileData = new char[FileLength];
DWORD BytesRead = 0;
if( SFileReadFile( SubFile, SubFileData, FileLength, &BytesRead, NULL ) )
{
CONSOLE_Print( "[MAP] overriding default common.j with map copy while calculating map_crc/sha1" );
OverrodeCommonJ = true;
Val = Val ^ XORRotateLeft( (unsigned char *)SubFileData, BytesRead );
m_GHost->m_SHA->Update( (unsigned char *)SubFileData, BytesRead );
}
delete [] SubFileData;
}
SFileCloseFile( SubFile );
}
}
if( !OverrodeCommonJ )
{
Val = Val ^ XORRotateLeft( (unsigned char *)CommonJ.c_str( ), CommonJ.size( ) );
m_GHost->m_SHA->Update( (unsigned char *)CommonJ.c_str( ), CommonJ.size( ) );
}
if( MapMPQReady )
{
HANDLE SubFile;
// override blizzard.j
if( SFileOpenFileEx( MapMPQ, "Scripts\\blizzard.j", 0, &SubFile ) )
{
uint32_t FileLength = SFileGetFileSize( SubFile, NULL );
if( FileLength > 0 && FileLength != 0xFFFFFFFF )
{
char *SubFileData = new char[FileLength];
DWORD BytesRead = 0;
if( SFileReadFile( SubFile, SubFileData, FileLength, &BytesRead, NULL ) )
{
CONSOLE_Print( "[MAP] overriding default blizzard.j with map copy while calculating map_crc/sha1" );
OverrodeBlizzardJ = true;
Val = Val ^ XORRotateLeft( (unsigned char *)SubFileData, BytesRead );
m_GHost->m_SHA->Update( (unsigned char *)SubFileData, BytesRead );
}
delete [] SubFileData;
}
SFileCloseFile( SubFile );
}
}
if( !OverrodeBlizzardJ )
{
Val = Val ^ XORRotateLeft( (unsigned char *)BlizzardJ.c_str( ), BlizzardJ.size( ) );
m_GHost->m_SHA->Update( (unsigned char *)BlizzardJ.c_str( ), BlizzardJ.size( ) );
}
Val = ROTL( Val, 3 );
Val = ROTL( Val ^ 0x03F1379E, 3 );
m_GHost->m_SHA->Update( (unsigned char *)"\x9E\x37\xF1\x03", 4 );
if( MapMPQReady )
{
vector<string> FileList;
FileList.push_back( "war3map.j" );
FileList.push_back( "scripts\\war3map.j" );
FileList.push_back( "war3map.w3e" );
FileList.push_back( "war3map.wpm" );
FileList.push_back( "war3map.doo" );
FileList.push_back( "war3map.w3u" );
FileList.push_back( "war3map.w3b" );
FileList.push_back( "war3map.w3d" );
FileList.push_back( "war3map.w3a" );
FileList.push_back( "war3map.w3q" );
bool FoundScript = false;
for( vector<string> :: iterator i = FileList.begin( ); i != FileList.end( ); ++i )
{
// don't use scripts\war3map.j if we've already used war3map.j (yes, some maps have both but only war3map.j is used)
if( FoundScript && *i == "scripts\\war3map.j" )
continue;
HANDLE SubFile;
if( SFileOpenFileEx( MapMPQ, (*i).c_str( ), 0, &SubFile ) )
{
uint32_t FileLength = SFileGetFileSize( SubFile, NULL );
if( FileLength > 0 && FileLength != 0xFFFFFFFF )
{
char *SubFileData = new char[FileLength];
DWORD BytesRead = 0;
if( SFileReadFile( SubFile, SubFileData, FileLength, &BytesRead, NULL ) )
{
if( *i == "war3map.j" || *i == "scripts\\war3map.j" )
FoundScript = true;
Val = ROTL( Val ^ XORRotateLeft( (unsigned char *)SubFileData, BytesRead ), 3 );
m_GHost->m_SHA->Update( (unsigned char *)SubFileData, BytesRead );
// DEBUG_Print( "*** found: " + *i );
}
delete [] SubFileData;
}
SFileCloseFile( SubFile );
}
else
{
// DEBUG_Print( "*** not found: " + *i );
}
}
if( !FoundScript )
CONSOLE_Print( "[MAP] couldn't find war3map.j or scripts\\war3map.j in MPQ file, calculated map_crc/sha1 is probably wrong" );
MapCRC = UTIL_CreateByteArray( Val, false );
CONSOLE_Print( "[MAP] calculated map_crc = " + UTIL_ByteArrayToDecString( MapCRC ) );
m_GHost->m_SHA->Final( );
unsigned char SHA1[20];
memset( SHA1, 0, sizeof( unsigned char ) * 20 );
m_GHost->m_SHA->GetHash( SHA1 );
MapSHA1 = UTIL_CreateByteArray( SHA1, 20 );
CONSOLE_Print( "[MAP] calculated map_sha1 = " + UTIL_ByteArrayToDecString( MapSHA1 ) );
}
else
CONSOLE_Print( "[MAP] unable to calculate map_crc/sha1 - map MPQ file not loaded" );
}
}
}
else
CONSOLE_Print( "[MAP] no map data available, using config file for map_size, map_info, map_crc, map_sha1" );
I don't understand why is it happens and is it possible to fix it by StormLib developers in new versions.
Please try to investigate to the issue.
Here's zipped StormLib which shows correct values (6.25): http://mvpro.net/share/github/StormLib-6.25.zip
Thanks in advance,
Alex.