236 lines
7.6 KiB
C
236 lines
7.6 KiB
C
#include "patch.h"
|
|
|
|
#include <Windows.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
bool is_hex_char(char chr) {
|
|
return ('0' <= chr && chr <= '9') || ('a' <= chr && chr <= 'f') || ('A' <= chr && chr <= 'F');
|
|
}
|
|
|
|
patch_t patch_sentinel = { .count = 0 };
|
|
patch_t* patch_list = &patch_sentinel;
|
|
|
|
void commit_patch(patch_t* patch) {
|
|
if (!patch->count) return;
|
|
|
|
patch_t* head = patch_list;
|
|
while (head->next) head = head->next;
|
|
head->next = patch;
|
|
patch->next = NULL;
|
|
}
|
|
|
|
bool skip_whitespace(FILE* fp, char* cChr) {
|
|
while (*cChr == ' ')
|
|
if (!fread(cChr, 1, 1, fp)) return false;
|
|
return true;
|
|
}
|
|
|
|
bool load_patches_file(const char* filename) {
|
|
FILE* fp;
|
|
if (fopen_s(&fp, filename, "r")) {
|
|
printf("Failed to open %s\n", filename);
|
|
return false;
|
|
}
|
|
|
|
parse_patches_state state = parse_patches_state_start;
|
|
char readBuffer[256];
|
|
size_t rbPtr = 0;
|
|
|
|
char cChr;
|
|
patch_t* patch = NULL;
|
|
bool sol = true;
|
|
while (1) {
|
|
if (!fread(&cChr, 1, 1, fp)) goto patch_parser_eof;
|
|
|
|
switch (state) {
|
|
case parse_patches_state_start:
|
|
if (cChr != '*' || !sol) {
|
|
if (cChr == '\r' || cChr == '\n')
|
|
sol = true;
|
|
else
|
|
sol = false;
|
|
continue;
|
|
}
|
|
|
|
patch = (patch_t*)malloc(sizeof *patch);
|
|
ZeroMemory(patch, sizeof *patch);
|
|
rbPtr = 0;
|
|
|
|
if (!fread(&cChr, 1, 1, fp)) goto patch_parser_eof;
|
|
state = parse_patches_state_addr;
|
|
if (!skip_whitespace(fp, &cChr)) goto patch_parser_eof;
|
|
// Falls through
|
|
|
|
case parse_patches_state_addr:
|
|
if (is_hex_char(cChr)) {
|
|
readBuffer[rbPtr++] = cChr;
|
|
break;
|
|
}
|
|
if (rbPtr != 8) {
|
|
printf("Invalid address length: %d\n", rbPtr);
|
|
return false;
|
|
}
|
|
hex_to_int(&(patch->address), readBuffer);
|
|
|
|
state = parse_patches_state_colon;
|
|
rbPtr = 0;
|
|
if (!skip_whitespace(fp, &cChr)) goto patch_parser_eof;
|
|
// Falls through
|
|
|
|
case parse_patches_state_colon:
|
|
if (cChr != ':') {
|
|
printf("Syntax Error: expected : saw %c\n", cChr);
|
|
return false;
|
|
}
|
|
if (!fread(&cChr, 1, 1, fp)) goto patch_parser_eof;
|
|
state = parse_patches_state_match;
|
|
if (!skip_whitespace(fp, &cChr)) goto patch_parser_eof;
|
|
// Falls through
|
|
|
|
case parse_patches_state_match:
|
|
if (is_hex_char(cChr)) {
|
|
readBuffer[rbPtr++] = cChr;
|
|
break;
|
|
}
|
|
|
|
if (rbPtr % 2 != 0) {
|
|
printf("Match length must be even: saw %d\n", rbPtr);
|
|
return false;
|
|
}
|
|
|
|
if (rbPtr != 0) {
|
|
patch->match = (unsigned char*)malloc(rbPtr / 2);
|
|
hex_to_bin(readBuffer, patch->match, rbPtr);
|
|
}
|
|
patch->count = rbPtr / 2;
|
|
|
|
state = parse_patches_state_gt;
|
|
rbPtr = 0;
|
|
if (!skip_whitespace(fp, &cChr)) goto patch_parser_eof;
|
|
// Falls through
|
|
|
|
case parse_patches_state_gt:
|
|
if (cChr != '>') {
|
|
printf("Syntax Error: expected > saw %c\n", cChr);
|
|
return false;
|
|
}
|
|
if (!fread(&cChr, 1, 1, fp)) goto patch_parser_eof;
|
|
state = parse_patches_state_replace;
|
|
if (!skip_whitespace(fp, &cChr)) goto patch_parser_eof;
|
|
// Falls through
|
|
|
|
case parse_patches_state_replace:
|
|
if (is_hex_char(cChr)) {
|
|
readBuffer[rbPtr++] = cChr;
|
|
break;
|
|
}
|
|
|
|
if (patch->count == 0) {
|
|
if (rbPtr == 0) {
|
|
printf("Replace length must be non-zero\n");
|
|
return false;
|
|
}
|
|
if (rbPtr % 2 != 0) {
|
|
printf("Replace length must be even: saw %d\n", rbPtr);
|
|
return false;
|
|
}
|
|
patch->count = rbPtr / 2;
|
|
|
|
patch->replace = (unsigned char*)malloc(rbPtr / 2);
|
|
hex_to_bin(readBuffer, patch->replace, rbPtr);
|
|
} else {
|
|
if (rbPtr != patch->count * 2) {
|
|
printf("Replace length must match: %d != %d\n", rbPtr, patch->count * 2);
|
|
return false;
|
|
}
|
|
|
|
patch->replace = (unsigned char*)malloc(patch->count);
|
|
hex_to_bin(readBuffer, patch->replace, rbPtr);
|
|
}
|
|
|
|
state = parse_patches_state_pound;
|
|
rbPtr = 0;
|
|
if (!skip_whitespace(fp, &cChr)) goto patch_parser_eof;
|
|
// Falls through
|
|
|
|
case parse_patches_state_pound:
|
|
if (cChr == '\r' || cChr == '\n') {
|
|
commit_patch(patch);
|
|
state = parse_patches_state_start;
|
|
break;
|
|
}
|
|
if (cChr != '#') {
|
|
printf("Syntax Error: expected # saw %c\n", cChr);
|
|
return false;
|
|
}
|
|
|
|
if (!fread(&cChr, 1, 1, fp)) goto patch_parser_eof;
|
|
state = parse_patches_state_name;
|
|
rbPtr = 0;
|
|
if (!skip_whitespace(fp, &cChr)) goto patch_parser_eof;
|
|
// Falls through
|
|
|
|
case parse_patches_state_name:
|
|
if (cChr == '\r' || cChr == '\n') {
|
|
patch->name = (char*)malloc(rbPtr + 1);
|
|
memcpy(patch->name, readBuffer, rbPtr);
|
|
patch->name[rbPtr] = '\0';
|
|
|
|
commit_patch(patch);
|
|
state = parse_patches_state_start;
|
|
break;
|
|
}
|
|
readBuffer[rbPtr++] = cChr;
|
|
break;
|
|
}
|
|
}
|
|
|
|
patch_parser_eof:
|
|
if (patch) commit_patch(patch);
|
|
fclose(fp);
|
|
return true;
|
|
}
|
|
|
|
bool load_patches(char* indexPath, char* exeName) {
|
|
FILE* fp;
|
|
if (fopen_s(&fp, indexPath, "r")) {
|
|
printf("Failed to open %s\n", indexPath);
|
|
return false;
|
|
}
|
|
|
|
char cChr;
|
|
char nameBuffer[256];
|
|
char patchBuffer[256];
|
|
size_t which = 0;
|
|
size_t nPtr = 0;
|
|
size_t pPtr = 0;
|
|
while (fread(&cChr, 1, 1, fp)) {
|
|
if (cChr == '\r' || cChr == '\n') {
|
|
if (nPtr && pPtr) {
|
|
nameBuffer[nPtr] = '\0';
|
|
patchBuffer[pPtr] = '\0';
|
|
if (strcmp(nameBuffer, exeName) == 0) {
|
|
printf("Loading: %s\n", patchBuffer);
|
|
if (!load_patches_file(patchBuffer)) return false;
|
|
}
|
|
}
|
|
nPtr = pPtr = which = 0;
|
|
continue;
|
|
}
|
|
|
|
if (cChr == ' ') continue;
|
|
if (cChr == ':') {
|
|
which++;
|
|
continue;
|
|
}
|
|
if (which == 0)
|
|
nameBuffer[nPtr++] = cChr;
|
|
else if (which == 1)
|
|
patchBuffer[pPtr++] = cChr;
|
|
}
|
|
|
|
fclose(fp);
|
|
return true;
|
|
}
|