// ObjectMergerFixer.cpp
// VERSION 4.1.1 [2016_11_05]
// ^ = MAJOR REBUILDS
// ^ = NEW STUFF
// ^ = FIXES
// BY SourceSeeker / EdwardElric (hiveworkshop.com) / MasterOfSickness (wc3c.net)
// THANKS TO:
// - Vexorian (AUTHOR OF JASSHELPER) FOR EVERYTHING :D
// - PitzerMike (AUTHOR OF GRIMEX) FOR PUBLISHING HIS SOURCE CODE
// - WaterKnight (http://www.hiveworkshop.com/members/waterknight.178519/) FOR HELPFUL TIPS VIA CHAT (http://www.hiveworkshop.com/chat/) & BEING PATIENT WITH ME & MY QUESTIONS ;p
// - Barade (http://www.hiveworkshop.com/members/barade.125524/) FOR HELPFUL TIPS ABOUT MPQ API
// - Aniki (http://www.hiveworkshop.com/members/aniki.241724/) FOR HINT TO ADD FIX FOR UNIT OBJECT FIELD (utco)
#include "sfmpq/SFmpqapi_no-lib.h" // HEADER FOR: MpqOpenArchiveForUpdate, ...
#include <iostream> // SYSTEM HEADER FOR: std::cout
#include <vector> // SYSTEM HEADER FOR: std::vector
#include <algorithm> // SYSTEM HEADER FOR: std::search
std::vector<char> getFixedW3U( std::vector<char> &v_input, const char * c_wrongBytes, const char * c_rightBytes ) {
std::vector<char> v_output;
std::vector<char>::iterator it_counter;
int i_distance;
int i_distance2 = 0;
// SEARCH FOR OBJECT FIELD ID [4 BYTES] & TYPE [4 BYTES] (EXAMPLE: "uaen" & STRING) IN BINARY STREAM AS HEX SEQUENCE: { 0x75, 0x61, 0x65, 0x6E, 0x03, 0x00, 0x00, 0x00 }
// OBJECT MERGER ERROR 1:
// THOSE LAST 4 BYTES SHOULD BE { 0x00, 0x00, 0x00, 0x00 }
// FOR EACH MATCH RETRIEVE THE FOLLOWING BYTES UNTIL NULL BYTE
// OBJECT MERGER ERROR 2:
// FOR "uaen" THIS BYTE SHOULD BE 0x00, 0x01, 0x02 OR 0x03, FOLLOWED BY 3 NULL BYTES,
// INSTEAD THE BYTE IS 0x48, 0x49, 0x50 OR 0x51, FOLLOWED BY 1 NULL BYTE!
// PUT CORRECT BYTES INTO OUTPUT VECTOR
it_counter = std::search( v_input.begin(), v_input.end(), c_wrongBytes, c_wrongBytes +8 );
// IF W3U CONTAINS WRONG BYTES LIKE "uaen....":
if ( it_counter != v_input.end() ) {
while ( it_counter != v_input.end() ) {
i_distance = it_counter - v_input.begin();
// APPEND PART OF INPUT VECTOR v_buffer TO OUTPUT VECTOR v_output:
v_output.insert( v_output.end(), v_input.begin() +i_distance2, v_input.begin() +i_distance +4 );
i_distance2 = i_distance +4;
// = EVERYTHING UNTIL (INLCUDING) ...uaen OR ...utco
// SAVE NUMBER FOLLOWED BY WRONG BYTES UNTIL NEXT NULL BYTE:
// SAVE UP TO TWO-PLACE (-9...99), BECAUSE:
// FIELD (utco) RANGE: -1 - 12
// FIELD (uaen) RANGE: 0 - 3
// - 0 = NOTHING
// - 1 = ONLY ATTACK 1
// - 2 = ONLY ATTACK 2
// - 3 = BOTH
// EXAMPLES:
// WRONG BYTES FOR uaen = 0:
// BYTE: 1 2 3 4 5 6 7 8 9 10
// CHAR: u a e n . . . . 0 .
// HEX: 0x75 0x61 0x65 0x6E 0x03 0x00 0x00 0x00 0x30 0x00
// RIGHT BYTES FOR uaen = 0:
// BYTE: 1 2 3 4 5 6 7 8 9 10 11 12
// CHAR: u a e n . . . . . . . .
// HEX: 0x75 0x61 0x65 0x6E 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
// WRONG BYTES FOR utco = 12:
// BYTE: 1 2 3 4 5 6 7 8 9 10 11
// CHAR: u t c o . . . . 1 2 .
// HEX: 0x75 0x74 0x63 0x6F 0x03 0x00 0x00 0x00 0x31 0x32 0x00
// RIGHT BYTES FOR utco = 12:
// BYTE: 1 2 3 4 5 6 7 8 9 10 11 12
// CHAR: u t c o . . . . . . . .
// HEX: 0x75 0x74 0x63 0x6F 0x00 0x00 0x00 0x00 0x0C 0x00 0x00 0x00
// WRONG BYTES FOR utco = -1:
// BYTE: 1 2 3 4 5 6 7 8 9 10 11
// CHAR: u t c o . . . . - 1 .
// HEX: 0x75 0x74 0x63 0x6F 0x03 0x00 0x00 0x00 0x2D 0x31 0x00
// RIGHT BYTES FOR utco = -1:
// BYTE: 1 2 3 4 5 6 7 8 9 10 11 12
// CHAR: u t c o . . . . ÿ ÿ ÿ ÿ
// HEX: 0x75 0x74 0x63 0x6F 0x00 0x00 0x00 0x00 0x255 0x255 0x255 0x255
int i_byteValue;
int i_byteCount = 0;
int i_matches = 0;
int i_sign = 1;
int i_result = 0;
// GET CHAR VALUE AT INDEX OF VECTOR:
while ( (i_byteValue = (int)v_input[i_distance +8 +i_byteCount]) != 0 ) {
// CHECK FOR MINUS:
if ( (i_byteValue == '-') && (i_matches == 0) ) {
i_sign = -1;
i_matches++;
}
// CHECK FOR DIGIT:
if ( (i_byteValue >= '0') && (i_byteValue <= '9') && (i_matches <= 2) ) {
i_result *= 10;
i_result += i_byteValue - '0';
i_matches++;
}
i_byteCount++;
}
i_result *= i_sign;
// ADD FIX:
if (i_result < 0) {
// CONVERT DECIMAL TO HEXADECIMAL:
// ONLY TESTED FOR -1!! DON'T KNOW IF ANYTHING LOWER WORKS AS IT SHOULD!! STILL HAS TO BE TESTED IF ANOTHER OBJECT FIELD WITH SUCH A RANGE < -1 SHOULD BE FIXED...
unsigned char bytes[4];
unsigned long n = i_result;
bytes[0] = (n >> 24) & 0xFF;
bytes[1] = (n >> 16) & 0xFF;
bytes[2] = (n >> 8) & 0xFF;
bytes[3] = n & 0xFF;
int dataArray[8] = { 0, 0, 0, 0, bytes[0], bytes[1], bytes[2], bytes[3] };
v_output.insert( v_output.end(), &dataArray[0], &dataArray[8] );
} else {
int dataArray[8] = { c_rightBytes[4], c_rightBytes[5], c_rightBytes[6], c_rightBytes[7], i_result, 0, 0, 0 };
v_output.insert( v_output.end(), &dataArray[0], &dataArray[8] );
}
i_distance2 = i_distance2 +4 +i_byteCount +1;
// i_distance2 +4 +i_byteCount +1 BECAUSE:
// u, t, c, o, 0x03, 0x00, 0x00, 0x00, \, 0, 0x00,
// ^ = distance2 ^ = +4 ^ = +i_byteCount
// CONTINUE SEARCH:
it_counter = std::search( v_input.begin() +i_distance +4, v_input.end(), c_wrongBytes, c_wrongBytes +8 );
}
// APPEND END OF INPUT VECTOR buffer TO OUTPUT VECTOR v_output:
v_output.insert( v_output.end(), v_input.begin() +i_distance2, v_input.end() );
return v_output;
}
return v_input;
}
int main(int argc, char **argv) {
funcMpqOpenArchiveForUpdate MpqOpenArchiveForUpdate = 0;
funcMpqCloseUpdatedArchive MpqCloseUpdatedArchive = 0;
funcMpqAddFileFromBufferEx MpqAddFileFromBufferEx = 0;
funcMpqAddFileFromBuffer MpqAddFileFromBuffer = 0;
funcSFileOpenFileEx SFileOpenFileEx = 0;
funcSFileGetFileSize SFileGetFileSize = 0;
funcSFileReadFile SFileReadFile = 0;
funcSFileCloseFile SFileCloseFile = 0;
HINSTANCE hSFMpq = 0;
hSFMpq = LoadLibrary("SFMpq.dll");
// "SFMpq.dll" FROM PitzerMike WAS BUILD AS 32-BIT DLL!
// IF YOU WANT TO USE THIS DLL, YOUR PROJECT HAS TO BE COMPILED AS 32-BIT TOO, OTHERWISE THE DLL ISN'T LOADED!
// IF DLL 'SFMpq.dll' WAS LOADED, CONTINUE:
if (hSFMpq!=0) {
MpqOpenArchiveForUpdate = (funcMpqOpenArchiveForUpdate)GetProcAddress(hSFMpq,"MpqOpenArchiveForUpdate");
MpqCloseUpdatedArchive = (funcMpqCloseUpdatedArchive)GetProcAddress(hSFMpq,"MpqCloseUpdatedArchive");
MpqAddFileFromBufferEx = (funcMpqAddFileFromBufferEx)GetProcAddress(hSFMpq,"MpqAddFileFromBufferEx");
MpqAddFileFromBuffer = (funcMpqAddFileFromBuffer)GetProcAddress(hSFMpq,"MpqAddFileFromBuffer");
SFileOpenFileEx = (funcSFileOpenFileEx)GetProcAddress(hSFMpq,"SFileOpenFileEx");
SFileGetFileSize = (funcSFileGetFileSize)GetProcAddress(hSFMpq,"SFileGetFileSize");
SFileReadFile = (funcSFileReadFile)GetProcAddress(hSFMpq,"SFileReadFile");
SFileCloseFile = (funcSFileCloseFile)GetProcAddress(hSFMpq,"SFileCloseFile");
HANDLE mpq = MpqOpenArchiveForUpdate(argv[1], MOAU_OPEN_EXISTING | MOAU_MAINTAIN_LISTFILE, 1024);
HANDLE file;
if(!SFileOpenFileEx(mpq, "war3map.w3u", 0, &file)) {
// DEBUG:
MessageBox(0, "fail: could not open 'war3map.w3u'", "ObjectMergerFixer: error", MB_OK);
std::cout << "fail: could not open 'war3map.w3u'\n" << "HIT ENTER TO CONTINUE!\n";
getchar();
return 0;
}
DWORD size = SFileGetFileSize(file, 0);
// Retrieves a size of the file within archive
// http://www.zezula.net/en/mpq/stormlib.html
char * c_buf = new char[size];
SFileReadFile(file, c_buf, size, &size, NULL);
// Reads data from the file
// http://www.zezula.net/en/mpq/stormlib.html
// CONTENT FROM FILE file IS SAVED TO buf.buf
SFileCloseFile(file);
// W3U FIXES:
// COPY INPUT W3U DATA INTO BUFFER:
std::vector<char> v_input( c_buf, c_buf +size );
// uaen:
const char c_uaen_wrong[9] = { 0x75, 0x61, 0x65, 0x6E, 0x03, 0x00, 0x00, 0x00 }; // { 0x75, 0x61, 0x65, 0x6E, ... } = { u, a, e, n, ... }
const char c_uaen_right[9] = { 0x75, 0x61, 0x65, 0x6E, 0x00, 0x00, 0x00, 0x00 }; // { 0x75, 0x61, 0x65, 0x6E, ... } = { u, a, e, n, ... }
std::vector<char> v_output = getFixedW3U( v_input, c_uaen_wrong, c_uaen_right );
// utco:
const char c_utco_wrong[9] = { 0x75, 0x74, 0x63, 0x6F, 0x03, 0x00, 0x00, 0x00 }; // { 0x75, 0x74, 0x63, 0x6F, ... } = { u, t, c, o, ... }
const char c_utco_right[9] = { 0x75, 0x74, 0x63, 0x6F, 0x00, 0x00, 0x00, 0x00 }; // { 0x75, 0x74, 0x63, 0x6F, ... } = { u, t, c, o, ... }
v_output = getFixedW3U( v_input, c_utco_wrong, c_utco_right );
// EXIT IF NO FIXES NEED TO BE APPLIED:
if ( v_output == v_input ) {
MpqCloseUpdatedArchive(mpq, 0);
return 0;
}
// ADD FIXED W3U BACK TO W3M/W3X:
if ( !MpqAddFileFromBufferEx(mpq, (LPVOID)v_output.data(), v_output.size(), "war3map.w3u", MAFA_REPLACE_EXISTING | MAFA_COMPRESS, MAFA_COMPRESS_DEFLATE, Z_BEST_COMPRESSION) ) {
// DEBUG:
MessageBox(0, "fail: could not write 'war3map.w3u'", "ObjectMergerFixer: error", MB_OK);
std::cout << "fail: could not write 'war3map.w3u'\n" << "HIT ENTER TO CONTINUE!\n";
getchar();
}
if ( !MpqCloseUpdatedArchive(mpq, 0) ) {
// DEBUG:
MessageBox(0, "fail: could not update MPQ", "ObjectMergerFixer: error", MB_OK);
std::cout << "fail: could not update MPQ\n" << "HIT ENTER TO CONTINUE!\n";
getchar();
}
// IF DLL 'SFMpq.dll' WAS NOT LOADED, EXIT:
} else {
// DEBUG:
MessageBox(0, "fail: could not load 'SFMpq.dll'", "ObjectMergerFixer: error", MB_OK);
std::cout << "fail: could not load 'SFMpq.dll'\n" << "HIT ENTER TO CONTINUE!\n";
getchar();
}
return 0;
}