MEXP(int) checkRevision(const char* formula, const char* files[], int numFiles, int mpqNumber, unsigned long* checksum) { long values[4], ovd[4], ovs1[4], ovs2[4]; char ops[4]; const char* token; int curFormula = 0; file_t f; uint8_t* file_buffer; uint32_t* dwBuf; uint32_t* current; size_t seed_count; #if DEBUG int i; bncsutil_debug_message_a("checkRevision(\"%s\", {", formula); for (i = 0; i < numFiles; i++) { bncsutil_debug_message_a("\t\"%s\",", files[i]); } bncsutil_debug_message_a("}, %d, %d, %p);", numFiles, mpqNumber, checksum); #endif if (!formula || !files || numFiles == 0 || mpqNumber <= 0 || !checksum) { bncsutil_debug_message("error: checkRevision() parameter sanity check " "failed"); return 0; } seed_count = checkrevision_seeds.size(); if (seed_count == 0) { initialize_checkrevision_seeds(); seed_count = checkrevision_seeds.size(); } if (seed_count <= (size_t) mpqNumber) { bncsutil_debug_message_a("error: no revision check seed value defined " "for MPQ number %d", mpqNumber); return 0; } token = formula; while (token && *token) { if (*(token + 1) == '=') { int variable = BUCR_GETNUM(*token); if (variable < 0 || variable > 3) { bncsutil_debug_message_a("error: Unknown revision check formula" " variable %c", *token); return 0; } token += 2; // skip over equals sign if (BUCR_ISNUM(*token)) { values[variable] = atol(token); } else { if (curFormula > 3) { // more than 4 operations? bloody hell. bncsutil_debug_message("error: Revision check formula" " contains more than 4 operations; unsupported."); return 0; } ovd[curFormula] = variable; ovs1[curFormula] = BUCR_GETNUM(*token); ops[curFormula] = *(token + 1); ovs2[curFormula] = BUCR_GETNUM(*(token + 2)); curFormula++; } } for (; *token != 0; token++) { if (*token == ' ') { token++; break; } } } // Actual hashing (yay!) // "hash A by the hashcode" values[0] ^= checkrevision_seeds[mpqNumber]; for (int i = 0; i < numFiles; i++) { size_t file_len, remainder, rounded_size, buffer_size; f = file_open(files[i], FILE_READ); if (!f) { bncsutil_debug_message_a("error: Failed to open file %s", files[i]); return 0; } file_len = file_size(f); remainder = file_len % 1024; rounded_size = file_len - remainder; file_buffer = (uint8_t*) file_map(f, file_len, 0); if (!file_buffer) { file_close(f); bncsutil_debug_message_a("error: Failed to map file %s into memory", files[i]); return 0; } if (remainder == 0) { // Mapped buffer may be used directly, without padding. dwBuf = (uint32_t*) file_buffer; buffer_size = file_len; } else { // Must be padded to nearest KB. size_t extra = 1024 - remainder; uint8_t pad = (uint8_t) 0xFF; uint8_t* pad_dest; buffer_size = file_len + extra; dwBuf = (uint32_t*) malloc(buffer_size); if (!dwBuf) { bncsutil_debug_message_a("error: Failed to allocate %d bytes " "of memory as a temporary buffer", buffer_size); file_unmap(f, file_buffer); file_close(f); return 0; } memcpy(dwBuf, file_buffer, file_len); file_unmap(f, file_buffer); file_buffer = (uint8_t*) 0; pad_dest = ((uint8_t*) dwBuf) + file_len; for (size_t j = file_len; j < buffer_size; j++) { *pad_dest++ = pad--; } } current = dwBuf; for (size_t j = 0; j < buffer_size; j += 4) { values[3] = LSB4(*(current++)); for (int k = 0; k < curFormula; k++) { switch (ops[k]) { case '+': values[ovd[k]] = values[ovs1[k]] + values[ovs2[k]]; break; case '-': values[ovd[k]] = values[ovs1[k]] - values[ovs2[k]]; break; case '^': values[ovd[k]] = values[ovs1[k]] ^ values[ovs2[k]]; break; case '*': // well, you never know values[ovd[k]] = values[ovs1[k]] * values[ovs2[k]]; break; case '/': // well, you never know values[ovd[k]] = values[ovs1[k]] / values[ovs2[k]]; break; default: // unrecognized operation // shit file_unmap(f, dwBuf); file_close(f); return 0; } } } if (file_buffer) file_unmap(f, file_buffer); else if (dwBuf && file_buffer == 0) free(dwBuf); // padded buffer file_close(f); } *checksum = (unsigned long) LSB4(values[2]); #if DEBUG bncsutil_debug_message_a("\tChecksum = %lu", *checksum); #endif return 1; }
int CheckRevision(const char *formula, const char *files[], int numFiles, int mpqNumber, uint32_t *checksum) { uint32_t values[4], ovd[4], ovs1[4], ovs2[4]; char ops[4]; const char *token; int curFormula, i, k, variable; unsigned char *file_buffer, *pad_dest, pad; uint32_t *dwBuf, *current; unsigned int file_len, remainder, rounded_size, buffer_size, j; #ifdef _WIN32 HANDLE hFile; HANDLE hFileMapping; #else FILE *f; #endif if (mpqNumber > 7 || mpqNumber < 0) return 1; curFormula = 0; token = formula; while (token && *token) { if (*(token + 1) == '=') { variable = BUCR_GETNUM(*token); if (variable < 0 || variable > 3) return 2; token += 2; if (BUCR_ISNUM(*token)) { values[variable] = strtoul(token, NULL, 10); } else { if (curFormula > 3) return 3; ovd[curFormula] = variable; ovs1[curFormula] = BUCR_GETNUM(*token); ops[curFormula] = *(token + 1); ovs2[curFormula] = BUCR_GETNUM(*(token + 2)); curFormula++; } } for (; *token != 0; token++) { if (*token == ' ') { token++; break; } } } values[0] ^= checksumseeds[mpqNumber]; for (i = 0; i < numFiles; i++) { #ifdef _WIN32 hFile = CreateFile(files[i], GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (!hFile) return 4; file_len = GetFileSize(hFile, NULL); hFileMapping = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL); if (!hFileMapping) return 5; file_buffer = (unsigned char *)MapViewOfFile(hFileMapping, FILE_MAP_READ, 0, 0, 0); #else f = fopen(files[i], "rb"); if (!f) return 4; fseek(f, 0, SEEK_END); file_len = ftell(f); rewind(f); file_buffer = (unsigned char *)mmap(NULL, file_len, PROT_READ, MAP_SHARED, fileno(f), 0); #endif if (!file_buffer) { #ifdef _WIN32 CloseHandle(hFile); #else fclose(f); #endif return 5; } remainder = file_len & 0x3FF; rounded_size = file_len - remainder; if (!remainder) { dwBuf = (uint32_t *)file_buffer; buffer_size = file_len; } else { size_t extra = 1024 - remainder; pad = (unsigned char)0xFF; buffer_size = file_len + extra; dwBuf = malloc(buffer_size); memcpy(dwBuf, file_buffer, file_len); #ifdef _WIN32 UnmapViewOfFile(file_buffer); CloseHandle(hFileMapping); #else munmap(file_buffer, file_len); #endif file_buffer = NULL; pad_dest = ((unsigned char *)dwBuf) + file_len; for (j = file_len; j < buffer_size; j++) *pad_dest++ = pad--; } current = dwBuf; for (j = 0; j < (buffer_size / sizeof(uint32_t)); j++) { values[3] = current[j]; for (k = 0; k < curFormula; k++) { switch (ops[k]) { case '+': values[ovd[k]] = values[ovs1[k]] + values[ovs2[k]]; break; case '-': values[ovd[k]] = values[ovs1[k]] - values[ovs2[k]]; break; case '^': values[ovd[k]] = values[ovs1[k]] ^ values[ovs2[k]]; break; default: #ifdef _WIN32 UnmapViewOfFile(dwBuf); CloseHandle(hFileMapping); CloseHandle(hFile); #else munmap(dwBuf, file_len); fclose(f); #endif return 6; } } } if (file_buffer) { #ifdef _WIN32 UnmapViewOfFile(file_buffer); CloseHandle(hFileMapping); #else munmap(file_buffer, file_len); #endif } else if (dwBuf && !file_buffer) { free(dwBuf); } #ifdef _WIN32 CloseHandle(hFile); #else fclose(f); #endif } *checksum = values[2]; return 0; }