// ObjectMergerFixer.cpp
// VERSION 4 [2016_10_22]
// BY SourceSeeker
// THANKS TO:
// - PitzerMike (AUTHOR OF GRIMEX) FOR PUBLISHING HIS SOURCE CODE
// - Barade (http://www.hiveworkshop.com/members/barade.125524/) FOR HELPFUL TIPS ON MPQ API
#include "sfmpq/SFmpqapi_no-lib.h" // HEADER FOR: MpqOpenArchiveForUpdate, ...
//#include <windows.h> // ALREADY INCLUDED IN: "sfmpq/SFmpqapi_no-lib.h"
#include <iostream> // SYSTEM HEADER FOR: cout
#include <fstream> // SYSTEM HEADER FOR: ofstream
#include <vector> // SYSTEM HEADER FOR: vector
#include <algorithm> // SYSTEM HEADER FOR: search
#include <sstream> // SYSTEM HEADER FOR: stringstream (TO CONCAT STRINGS)
using namespace std;
// MORE USEFUL STL FUNCTIONS & COMMENTS FOR "function_getOutputVector" IN MY VERSION 3:
// D:\Privat\Anwendung\Programm\CODING\C\dev-cpp\c_warcraft\ver3_readFileBinary,HexEdit,NewFile.cpp
// C++ DLL (= Dynamic Linked Library) TUTORIAL:
// http://www.codeguru.com/cpp/cpp/cpp_mfc/tutorials/article.php/c9855/DLL-Tutorial-For-Beginners.htm
vector<char> function_getOutputVector( char * c_buf, int size ) {
// copies all data into buffer:
vector<char> v_buffer( c_buf, c_buf +size );
// How to convert char* to std::vector:
// http://stackoverflow.com/questions/4272445/how-to-convert-char-to-stdvector
// TUTORIAL:
// http://www.cprogramming.com/tutorial/stl/vector.html
// std::vector<int> vec_five(aList.begin(), aList.end());
// A vector can also be initialized using elements from another collection, by means of a beginning and ending iterator pair.
// The arguments can be any form of iterator; thus collections can be initialized with values drawn from any of the container classes in the C++ Standard Library that support iterators.
// http://stdcxx.apache.org/doc/stdlibug/5-2.html
vector<char> v_output;
vector<char>::iterator it_counter;
const char c_uaenId[9] = { 0x75, 0x61, 0x65, 0x6E, 0x03, 0x00, 0x00, 0x00 }; // { 0x75, 0x61, 0x65, 0x6E, ... } = { u, a, e, n, ... }
int i_uaen_getValue;
int i_distance;
int i_distance2 = 0;
// SEARCH FOR "uaen" 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 BYTE WHICH CAN BE 0x48, 0x49, 0x50 OR 0x51 (0, 1, 2 OR 3)
// OBJECT MERGER ERROR 2:
// 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 = search( v_buffer.begin(), v_buffer.end(), c_uaenId, c_uaenId + 8 );
// http://stackoverflow.com/questions/16465087/is-there-a-function-that-is-to-stdsearch-what-stdcount-is-to-stdfind
// IF W3U DOESN'T CONTAIN "uaen....", EXIT:
if ( it_counter == v_buffer.end() ) {
return vector<char>();
//return {};
// COMPILER: [Warning] extended initializer lists only available with -std=c++11 or -std=gnu++11
// return an empty vector
// EITHER USE: return {};
// OR: return vector<char>();
// http://stackoverflow.com/questions/24175507/return-an-empty-vector-c
//return 0;
// USED THIS WHEN IN FUNCTION main
// IF W3U CONTAINS "uaen....", INSPECT FURTHER:
} else {
while ( it_counter != v_buffer.end() ) {
//if ( find(buffer.begin(), buffer.end(), 0x03) != buffer.end() ) {
// find(vector.begin(), vector.end(), item)
// http://www.cplusplus.com/reference/algorithm/find/
// http://stackoverflow.com/questions/571394/how-to-find-if-an-item-is-present-in-a-stdvector
// http://gamedev.stackexchange.com/questions/114201/check-if-a-vector-element-exists-using-stdfind
i_distance = it_counter - v_buffer.begin();
// Effective way to get the index of an iterator?
// INSTEAD OF: distance(vec.begin(), it)
// USE: it - vec.begin()
// http://stackoverflow.com/questions/2152986/effective-way-to-get-the-index-of-an-iterator
//i_distance = distance( v_buffer.begin(), it_counter );
// How do I find an element position in std::vector
// http://stackoverflow.com/questions/1425349/how-do-i-find-an-element-position-in-stdvector
// APPEND PART OF INPUT VECTOR buffer TO OUTPUT VECTOR v_output:
// http://stackoverflow.com/questions/2551775/appending-a-vector-to-a-vector
v_output.insert( v_output.end(), v_buffer.begin() +i_distance2, v_buffer.begin() +i_distance +4 );
// insert (iterator position, InputIterator first, InputIterator last);
// http://www.cplusplus.com/reference/vector/vector/insert/
// .insert VS .at VS []
// If we now print out vec.insert(vec.begin()+2, 10); we will get:
// 1 2 10 3 4 5 0 0
// If, instead, we did vec.at(2) = 10 , or vec[2]=10 , we would get
// 1 2 10 4 5 0 0 0
// http://stackoverflow.com/questions/6726805/insert-an-element-into-a-specific-position-of-a-vector
// + i + 4
// http://stackoverflow.com/questions/421573/best-way-to-extract-a-subvector-from-a-vector
i_distance2 = i_distance +4;
// = EVERYTHING UNTIL (INLCUDING) ...uaen
// GET uaen VALUE:
// CAN BE: 0, 1, 2 OR 3
// - 0 = NOTHING
// - 1 = ONLY ATTACK 1
// - 2 = ONLY ATTACK 2
// - 3 = BOTH
// GET CHAR VALUE AT INDEX OF VECTOR:
// SOLUTION (JUST CAST TO int):
// http://www.cplusplus.com/reference/vector/vector/operator%5B%5D/
// http://cplusplusio0.blogspot.de/2015/03/convert-element-of-vector-into-type-t-c.html
// http://stackoverflow.com/questions/6660145/convert-ascii-number-to-ascii-character-in-c
i_uaen_getValue = (int)v_buffer[i_distance +8];
// WRONG ATTEMPTS:
//c_uaen_getValue = v_buffer[i_distance+8];
//c_uaen_getValue = v_buffer.at(i_distance+8);
//c_uaen_getValue = static_cast<char>(v_buffer[i_distance+8]);
// int CONVERSION: ASCII VALUE OF NUMBER TO NUMBER:
i_uaen_getValue = i_uaen_getValue - '0';
// EXAMPLE:
// '0' -> 0, '1' -> 1, ...
// http://stackoverflow.com/questions/5029840/convert-char-to-int-in-c-and-c
// ADD CHAR ARRAY AT END OF VECTOR:
// ANSWER FROM "MattyT":
// http://stackoverflow.com/questions/259297/how-do-you-copy-the-contents-of-an-array-to-a-stdvector-in-c-without-looping
int dataArray[8] = { 0, 0, 0, 0, i_uaen_getValue, 0, 0, 0 };
v_output.insert( v_output.end(), &dataArray[0], &dataArray[8] );
// INSTEAD OF USING SEVERAL VECTOR.push_back() LIKE:
//v_output.push_back('0');
i_distance2 = i_distance2 +6;
it_counter = search( v_buffer.begin() +i_distance+4, v_buffer.end(), c_uaenId, c_uaenId +4 );
}
// APPEND END OF INPUT VECTOR buffer TO OUTPUT VECTOR v_output:
v_output.insert( v_output.end(), v_buffer.begin() +i_distance2, v_buffer.end() );
return v_output;
}
return vector<char>();
//return {};
// COMPILER: [Warning] extended initializer lists only available with -std=c++11 or -std=gnu++11
// return an empty vector
// EITHER USE: return {};
// OR: return vector<char>();
// http://stackoverflow.com/questions/24175507/return-an-empty-vector-c
}
int main(int argc, char **argv) {
funcMpqOpenArchiveForUpdate MpqOpenArchiveForUpdate = 0;
funcMpqCloseUpdatedArchive MpqCloseUpdatedArchive = 0;
funcMpqAddFileToArchive MpqAddFileToArchive = 0;
//MpqAddFileToArchiveEx = 0;
funcMpqAddFileFromBufferEx MpqAddFileFromBufferEx = 0;
funcMpqAddFileFromBuffer MpqAddFileFromBuffer = 0;
funcMpqCompactArchive MpqCompactArchive = 0;
funcSFileOpenFileEx SFileOpenFileEx = 0;
funcSFileGetFileSize SFileGetFileSize = 0;
funcSFileReadFile SFileReadFile = 0;
funcSFileCloseFile SFileCloseFile = 0;
// SHOW MESSAGE BOX WITH ALL argv ARGUMENTS:
// CONCAT STRINGS:
// http://stackoverflow.com/questions/662918/how-do-i-concatenate-multiple-c-strings-on-one-line
std::stringstream ss;
//ss << "argc = " << argc << "\nargv[0] = " << argv[0] << "\nargv[1] = " << argv[1] << "\nargv[2] = " << argv[2];
//std::string s = ss.str();
// STEP THROUGH ARRAY argv & PRINT ALL COMMAND-LINE ARGUMENTS:
// http://www.site.uottawa.ca/~lucia/courses/2131-05/labs/Lab3/CommandLineArguments.html
for(int i=0; i<argc; i++)
ss << "argv[" << i << "] = " << argv[i] << "\n";
std::string s = ss.str();
// CREATE A MESSAGE BOX VIA WINDOWS API:
// https://social.msdn.microsoft.com/Forums/de-DE/d70a77b7-1508-4884-a5bc-106cf068b1be/how-can-i-show-messagebox-in-visual-c?forum=vcgeneral
//MessageBox(0, "And text here", "MessageBox caption", MB_OK);
//MessageBox(0, s.c_str(), "MessageBox caption", MB_OK);
// RETURNS:
// argv[0] = grimext\ObjectMergerFixer.exe
// argv[1] = C:\Spiele\Warcraft III\Maps\_SourceSeeker\test_objectMergerW3u.w3m
// argv[2] = ./jass/
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!
// SEE: "_dev-c++_setting & sfmpq.png"
if (hSFMpq!=0) {
// DEBUG:
//cout << "ok: DLL loaded\n" << "HIT ENTER TO CONTINUE!\n";
//getchar();
MpqOpenArchiveForUpdate = (funcMpqOpenArchiveForUpdate)GetProcAddress(hSFMpq,"MpqOpenArchiveForUpdate");
MpqCloseUpdatedArchive = (funcMpqCloseUpdatedArchive)GetProcAddress(hSFMpq,"MpqCloseUpdatedArchive");
MpqAddFileToArchive = (funcMpqAddFileToArchive)GetProcAddress(hSFMpq,"MpqAddFileToArchive");
//MpqAddFileToArchiveEx = (funcMpqAddFileToArchiveEx)GetProcAddress(hSFMpq,"MpqAddFileToArchiveEx");
MpqAddFileFromBufferEx = (funcMpqAddFileFromBufferEx)GetProcAddress(hSFMpq,"MpqAddFileFromBufferEx");
MpqAddFileFromBuffer = (funcMpqAddFileFromBuffer)GetProcAddress(hSFMpq,"MpqAddFileFromBuffer");
MpqCompactArchive = (funcMpqCompactArchive)GetProcAddress(hSFMpq,"MpqCompactArchive");
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 mpq = MpqOpenArchiveForUpdate("C:\\Spiele\\Warcraft III\\Maps\\_SourceSeeker\\test_objectMergerW3u.w3m", MOAU_OPEN_EXISTING | MOAU_MAINTAIN_LISTFILE, 1024);
// HANDLE
// = typdef LPVOID HANDLE
// DEFINED IN: "sfmpq/SFmpqapi_no-lib.h", DEFINED IN: <windows.h>
// MpqOpenArchiveForUpdate
// USED IN: PitzerMike TOOLS IN "ObjectMerger/main.cpp":
//HANDLE mpq = MpqOpenArchiveForUpdate(argv[1], MOAU_OPEN_EXISTING | MOAU_MAINTAIN_LISTFILE, 1024);
// = typedef MPQHANDLE (WINAPI* funcMpqOpenArchiveForUpdate)(LPCSTR lpFileName, DWORD dwFlags, DWORD dwMaximumFilesInArchive);
// DEFINED IN: "sfmpq/SFmpqapi_no-lib.h"
// DEFINED IN: "sfmpq/SFmpqapi_no-lib.cpp"
// = MPQHANDLE SFMPQAPI WINAPI MpqOpenArchiveForUpdate(LPCSTR lpFileName, DWORD dwFlags, DWORD dwMaximumFilesInArchive)
// DEFINED IN:
// https://github.com/ShadowFlare/SFmpqapi/blob/master/SFmpqapi.cpp
// argv[1]
// = ABSOLUTE PATH TO WARCRAFT MAP
// EXAMPLE: C:\Spiele\Warcraft III\Maps\_SourceSeeker\test_objectMergerW3u.w3m
// MOAU_OPEN_EXISTING
// = #define MOAU_OPEN_EXISTING 0x04
// DEFINED IN: "sfmpq/SFmpqapi_no-lib.h"
// MOAU_MAINTAIN_LISTFILE
// = #define MOAU_MAINTAIN_LISTFILE 0x01
// DEFINED IN: "sfmpq/SFmpqapi_no-lib.h"
HANDLE file;
// DEFINED IN: "sfmpq/SFmpqapi_no-lib.h"
// AS: #include <windows.h> // = HEADER FOR: HANDLE = typdef LPVOID HANDLE
if(!SFileOpenFileEx(mpq, "war3map.w3u", 0, &file)) {
// DEBUG:
MessageBox(0, "fail: could not open 'war3map.w3u'", "error", MB_OK);
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);
// FIX W3U uaen FIELDS:
vector<char> v_output = function_getOutputVector(c_buf, size);
// DEBUG:
//if ( v_output.empty() ) {
// cout << "map's W3U not touched; no wrong 'uaen....' found!\n" << "HIT ENTER TO CONTINUE!\n";
// getchar();
//}
// ADD FIXED W3U BACK TO W3M/W3X:
if ( !MpqAddFileFromBufferEx(mpq, (LPVOID)&v_output[0], 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'", "error", MB_OK);
cout << "fail: could not write 'war3map.w3u'\n" << "HIT ENTER TO CONTINUE!\n";
getchar();
}
// USED IN: PitzerMike TOOLS IN "common/io.cpp":
// result = MpqAddFileFromBufferEx(mpq, (LPVOID)buf, length, filename, MAFA_REPLACE_EXISTING | MAFA_COMPRESS, MAFA_COMPRESS_DEFLATE, Z_BEST_COMPRESSION);
// API:
// BOOL SFMPQAPI WINAPI MpqAddFileFromBufferEx(MPQHANDLE hMPQ, LPVOID lpBuffer, DWORD dwLength, LPCSTR lpFileName, DWORD dwFlags, DWORD dwCompressionType, DWORD dwCompressLevel)
// https://github.com/ShadowFlare/SFmpqapi/blob/master/SFmpqapi.cpp
//MpqAddFileFromBufferEx(mpq, (LPVOID)c_buf, size, "war3map.w3u", MAFA_REPLACE_EXISTING | MAFA_COMPRESS, MAFA_COMPRESS_DEFLATE, Z_BEST_COMPRESSION);
//MpqAddFileToArchive(mpq, c_buf, "war3map.w3u", MAFA_REPLACE_EXISTING | MAFA_COMPRESS);
// MpqAddFileToArchive(...)
// USED IN: PitzerMike TOOLS IN "common/io.cpp":
// bool filetoarchive(const char *source, HANDLE &mpq, const char* filename) {
// ...
// result = MpqAddFileToArchiveEx(mpq, source, filename, MAFA_REPLACE_EXISTING | MAFA_COMPRESS, MAFA_COMPRESS_DEFLATE, Z_BEST_COMPRESSION);
// API:
// BOOL SFMPQAPI WINAPI MpqAddFileToArchive(MPQHANDLE hMPQ, LPCSTR lpSourceFileName, LPCSTR lpDestFileName, DWORD dwFlags)
// {
// return MpqAddFileToArchiveEx(hMPQ,lpSourceFileName,lpDestFileName,dwFlags,0x08,0);
// }
// https://github.com/ShadowFlare/SFmpqapi/blob/master/SFmpqapi.cpp
// MAFA_REPLACE_EXISTING | MAFA_COMPRESS
// MAFA
// = Mpq Add File to Archive
//MpqAddFileToArchive(mpq, &v_output[0], "war3map.w3u", MAFA_REPLACE_EXISTING | MAFA_COMPRESS);
// How to convert vector to array C++:
//std::vector<double> v;
//double* a = &v[0];
// http://stackoverflow.com/questions/2923272/how-to-convert-vector-to-array-c
// DEBUG:
ofstream ofs_file1( "_out1.txt", ios::out | ios::binary );
ofstream ofs_file2( "_out2.txt", ios::out | ios::binary );
ofs_file1.write( c_buf, size );
ofs_file2.write( &v_output[0], v_output.size() );
// write (const char* s, streamsize n);
// Inserts the first n characters of the array pointed by s into the stream
// http://www.cplusplus.com/reference/ostream/ostream/write/
//MpqCompactArchive(mpq);
if ( !MpqCloseUpdatedArchive(mpq, 0) ) {
// DEBUG:
MessageBox(0, "fail: could not update MPQ", "error", MB_OK);
cout << "fail: could not update MPQ\n" << "HIT ENTER TO CONTINUE!\n";
getchar();
}
} else {
// DEBUG:
MessageBox(0, "fail: could not load 'SFMpq.dll'", "error", MB_OK);
cout << "fail: could not load 'SFMpq.dll'\n" << "HIT ENTER TO CONTINUE!\n";
getchar();
}
// DEBUG:
MessageBox(0, "reached end of code of ObjectMergerFixer", "debug", MB_OK);
return 0;
}