|
| 1 | +#define NDEBUG |
| 2 | +#include <fstream> |
| 3 | +#include <cassert> |
| 4 | +#include <tuple> |
| 5 | +#include <iomanip> |
| 6 | +#include <iostream> |
| 7 | +#include <cstdlib> |
| 8 | +#include <cstring> |
| 9 | +#include <string> |
| 10 | +#include <sstream> |
| 11 | +#include <vector> |
| 12 | + |
| 13 | +using namespace std; |
| 14 | + |
| 15 | +uint8_t hexSubStringToByte(const string& hexString, int start, int length) { |
| 16 | + auto subStr = hexString.substr(start, length); |
| 17 | + return strtol(subStr.c_str(), NULL, 16); |
| 18 | +} |
| 19 | + |
| 20 | +vector<uint8_t> GUIDStringToByteArray(const string& GUID) { |
| 21 | + vector<uint8_t> out(16); |
| 22 | + out[3] = hexSubStringToByte(GUID, 1, 2); |
| 23 | + out[2] = hexSubStringToByte(GUID, 3, 2); |
| 24 | + out[1] = hexSubStringToByte(GUID, 5, 2); |
| 25 | + out[0] = hexSubStringToByte(GUID, 7, 2); |
| 26 | + out[5] = hexSubStringToByte(GUID, 10, 2); |
| 27 | + out[4] = hexSubStringToByte(GUID, 12, 2); |
| 28 | + out[7] = hexSubStringToByte(GUID, 15, 2); |
| 29 | + out[6] = hexSubStringToByte(GUID, 17, 2); |
| 30 | + out[8] = hexSubStringToByte(GUID, 20, 2); |
| 31 | + out[9] = hexSubStringToByte(GUID, 22, 2); |
| 32 | + out[10] = hexSubStringToByte(GUID, 25, 2); |
| 33 | + out[11] = hexSubStringToByte(GUID, 27, 2); |
| 34 | + out[12] = hexSubStringToByte(GUID, 29, 2); |
| 35 | + out[13] = hexSubStringToByte(GUID, 31, 2); |
| 36 | + out[14] = hexSubStringToByte(GUID, 33, 2); |
| 37 | + out[15] = hexSubStringToByte(GUID, 35, 2); |
| 38 | + return out; |
| 39 | +} |
| 40 | + |
| 41 | +string toHexString(int val, int width) { |
| 42 | + string valueHexString; |
| 43 | + stringstream ss; |
| 44 | + ss << setfill('0') << setw(width) << hex << val; |
| 45 | + return ss.str(); |
| 46 | +} |
| 47 | + |
| 48 | +// Input: { 0xFF, 0xAA, 0xCC } |
| 49 | +// Ouput: FFAACC |
| 50 | +string ByteArrayToString(vector<uint8_t> bytes) { |
| 51 | + stringstream ss; |
| 52 | + for(int i = 0; i < 16; i++) { |
| 53 | + ss << toHexString(bytes[i], 2); |
| 54 | + } |
| 55 | + return ss.str(); |
| 56 | +} |
| 57 | + |
| 58 | +// Input is a GUID how it appears in binary format in the file, |
| 59 | +// output is a human readable string how that GUID would appear in the GUIDs.txt |
| 60 | +// string ByteArrayToGUIDString(char* start) { |
| 61 | +string ByteArrayToGUIDString(vector<uint8_t> bytes) { |
| 62 | + stringstream ss; |
| 63 | + ss << "{"; |
| 64 | + ss << toHexString(bytes[3] & 0xFF, 2); |
| 65 | + ss << toHexString(bytes[2] & 0xFF, 2); |
| 66 | + ss << toHexString(bytes[1] & 0xFF, 2); |
| 67 | + ss << toHexString(bytes[0] & 0xFF, 2); |
| 68 | + ss << '-'; |
| 69 | + ss << toHexString(bytes[5] & 0xFF, 2); |
| 70 | + ss << toHexString(bytes[4] & 0xFF, 2); |
| 71 | + ss << '-'; |
| 72 | + ss << toHexString(bytes[7] & 0xFF, 2); |
| 73 | + ss << toHexString(bytes[6] & 0xFF, 2); |
| 74 | + ss << '-'; |
| 75 | + ss << toHexString(bytes[8] & 0xFF, 2); |
| 76 | + ss << toHexString(bytes[9] & 0xFF, 2); |
| 77 | + ss << '-'; |
| 78 | + ss << toHexString(bytes[10] & 0xFF, 2); |
| 79 | + ss << toHexString(bytes[11] & 0xFF, 2); |
| 80 | + ss << toHexString(bytes[12] & 0xFF, 2); |
| 81 | + ss << toHexString(bytes[13] & 0xFF, 2); |
| 82 | + ss << toHexString(bytes[14] & 0xFF, 2); |
| 83 | + ss << toHexString(bytes[15] & 0xFF, 2); |
| 84 | + ss << '}'; |
| 85 | + return ss.str(); |
| 86 | +} |
| 87 | + |
| 88 | +bool isHexString(const string& s) { |
| 89 | + return s.find_first_not_of("0123456789abcdefABCDEF", 2) == string::npos; |
| 90 | +} |
| 91 | + |
| 92 | +bool isValidGUIDString(const string &s) { |
| 93 | + if(s[0] != '{' || s[37] != '}') { |
| 94 | + return false; |
| 95 | + } else if(!isHexString(s.substr(1, 8))) { |
| 96 | + return false; |
| 97 | + } else if(s[9] != '-') { |
| 98 | + return false; |
| 99 | + } else if(!isHexString(s.substr(9, 4))) { |
| 100 | + return false; |
| 101 | + } else if(s[14] != '-') { |
| 102 | + return false; |
| 103 | + } else if(!isHexString(s.substr(14, 4))) { |
| 104 | + return false; |
| 105 | + } else if(s[19] != '-') { |
| 106 | + return false; |
| 107 | + } else if(!isHexString(s.substr(19, 4))) { |
| 108 | + return false; |
| 109 | + } else if(s[24] != '-') { |
| 110 | + return false; |
| 111 | + } else if(!isHexString(s.substr(24, 12))) { |
| 112 | + return false; |
| 113 | + } |
| 114 | + return true; |
| 115 | +} |
| 116 | + |
| 117 | +void test_isValidGUIDString() { |
| 118 | + assert(isValidGUIDString("{c7d655d8-ec85-4d25-bc91-cf2cca64fbb7}") == true); |
| 119 | + assert(isValidGUIDString("{RRRRRRRR-ec85-4d25-bc91-cf2cca64fbb7}") == false); |
| 120 | + assert(isValidGUIDString("{c7d655d8-ec85-4d25-bc91-cf2cca64fbb7") == false); |
| 121 | + assert(isValidGUIDString("c7d655d8-ec85-4d25-bc91-cf2cca64fbb7") == false); |
| 122 | +} |
| 123 | + |
| 124 | +void test_toHextString() { |
| 125 | + assert(toHexString(0, 2).compare(0, 2, "00") == 0); |
| 126 | + assert(toHexString(0, 1).compare(0, 2, "0") == 0); |
| 127 | + assert(toHexString(15, 2).compare(0, 2, "0f") == 0); |
| 128 | + assert(toHexString(15, 1).compare(0, 2, "f") == 0); |
| 129 | +} |
| 130 | + |
| 131 | +void test_hexSubStringToByte() { |
| 132 | + assert(hexSubStringToByte("FFAA", 1, 1) == 15); |
| 133 | + assert(hexSubStringToByte("FFAA", 1, 2) == 250); |
| 134 | + assert(hexSubStringToByte("FFAA", 0, 1) == 15); |
| 135 | + assert(hexSubStringToByte("FFAA", 0, 2) == 255); |
| 136 | + assert(hexSubStringToByte("FFAA", 2, 2) == 170); |
| 137 | +} |
| 138 | + |
| 139 | +void test_GUIDStringToByteArray() { |
| 140 | +const string testGUID = "{c7d655d8-ec85-4d25-bc91-cf2cca64fbb7}"; |
| 141 | + const vector<uint8_t> correctOutput = { 216, 85, 214, 199, 133, 236, 37, 77, 188, 145, 207, 44, 202, 100, 251, 183 }; |
| 142 | + auto bytes = GUIDStringToByteArray(testGUID); |
| 143 | + for(int i = 0; i < 16; i++) { |
| 144 | + assert(correctOutput[i] == bytes[i]); |
| 145 | + } |
| 146 | +} |
| 147 | + |
| 148 | +void test_ConversionBackAndForth() { |
| 149 | + const string testGUID = "{c7d655d8-ec85-4d25-bc91-cf2cca64fbb7}"; |
| 150 | + assert(ByteArrayToGUIDString(GUIDStringToByteArray(testGUID)) == testGUID); |
| 151 | +} |
| 152 | + |
| 153 | +void run_tests() { |
| 154 | + test_toHextString(); |
| 155 | + test_hexSubStringToByte(); |
| 156 | + test_GUIDStringToByteArray(); |
| 157 | + test_ConversionBackAndForth(); |
| 158 | + test_isValidGUIDString(); |
| 159 | +} |
| 160 | + |
| 161 | +int wrongUsage() { |
| 162 | + cerr << "Usage: guid_replacer.exe your_audio.bank [GUID to replace] [what to replace it with]" << endl; |
| 163 | + cerr << "Example: guid_replacer.exe your_audio.bank {bc5e80f9-a380-47c9-8799-3e02eea94feb} {aabbccdd-eeff-00ff-aabb-feffefbbaacc}" << endl; |
| 164 | + cerr << "Multiple replacements can be chained like this:" << endl; |
| 165 | + cerr << "guid_replacer.exe your_audio.bank GUID_A1 GUID_A2 GUID_B1 GUID_B2" << endl; |
| 166 | + return -1; |
| 167 | +} |
| 168 | + |
| 169 | +int main(int argc, char** argv) { |
| 170 | + run_tests(); |
| 171 | + vector<tuple<string, string>> guidReplacePairs; |
| 172 | + if(argc % 2 == 1) { |
| 173 | + return wrongUsage(); |
| 174 | + } |
| 175 | + const string filename = argv[1]; |
| 176 | + if(filename.find_first_of(".bank") == string::npos) { |
| 177 | + return wrongUsage(); |
| 178 | + } |
| 179 | + for(int i = 2; i < argc; i += 2) { |
| 180 | + if(!isValidGUIDString(argv[i]) || !isValidGUIDString(argv[i+1])) { |
| 181 | + return wrongUsage(); |
| 182 | + } |
| 183 | + guidReplacePairs.push_back(make_tuple(argv[i], argv[i+1])); |
| 184 | + } |
| 185 | + fstream new_file(filename, ios::in | ios:: out | ios::binary); |
| 186 | + const char* EVNTEVTB = "EVNTEVTB"; |
| 187 | + if(!new_file) { |
| 188 | + cout << "Could not open file '" << filename << "': " << strerror(errno) << endl; |
| 189 | + } else { |
| 190 | + // First find the offset where the string EVNTEVTB ends |
| 191 | + int currentMatchingPosition = 0; |
| 192 | + while(!new_file.eof()) { |
| 193 | + const size_t BUFFER_SIZE = 1024; |
| 194 | + string buffer(BUFFER_SIZE, '\0'); |
| 195 | + int pos = new_file.tellg(); |
| 196 | + new_file.read(&buffer[0], BUFFER_SIZE); |
| 197 | + for(int i = 0; i < BUFFER_SIZE; i++) { |
| 198 | + if(buffer[i] == EVNTEVTB[currentMatchingPosition]) { |
| 199 | + currentMatchingPosition++; |
| 200 | + if(currentMatchingPosition == 8) { |
| 201 | + currentMatchingPosition = 0; |
| 202 | + // End of EVNTEVTB string found at pos + i + 1, GUID starts 4 bytes later |
| 203 | + int GUIDstartPos = pos + i + 1 + 4; |
| 204 | + // Read out the GUID |
| 205 | + vector<uint8_t> GUID(17); |
| 206 | + GUID[16] = 0; |
| 207 | + buffer.copy((char*)&GUID[0], 16, i + 1 + 4); |
| 208 | + // cout << "Found a GUID at offset " << GUIDstartPos << " - " << ByteArrayToGUIDString(GUID) << " - " << ByteArrayToString(GUID) << endl; |
| 209 | + for(auto it = guidReplacePairs.begin(); it != guidReplacePairs.end(); it++) { |
| 210 | + const string lookFor = get<0>(*it); |
| 211 | + const string replaceWith = get<1>(*it); |
| 212 | + if(lookFor.compare(ByteArrayToGUIDString(GUID)) == 0) { |
| 213 | + cout << "Replacing " << lookFor << " at offset " << GUIDstartPos << " with " << replaceWith << endl; |
| 214 | + new_file.seekp(GUIDstartPos); |
| 215 | + auto boop = GUIDStringToByteArray(replaceWith); |
| 216 | + new_file.write((char*)&boop[0], 16); |
| 217 | + } |
| 218 | + } |
| 219 | + } |
| 220 | + } else { |
| 221 | + currentMatchingPosition = 0; |
| 222 | + } |
| 223 | + } |
| 224 | + } |
| 225 | + new_file.close(); |
| 226 | + } |
| 227 | + return 0; |
| 228 | +} |
0 commit comments