#include "patch.h" #include #include bool fetch(json_value* object, char* name, json_value** value) { if (object->type != json_object) return false; for (size_t j = 0; j < object->u.object.length; j++) { json_object_entry* entry = &(object->u.object.values[j]); if (strncmp(name, entry->name, entry->name_length) == 0) { *value = entry->value; return true; } } return false; } bool fetch_string(json_value* object, char* name, char** value) { json_value* fetched; if (!fetch(object, name, &fetched)) return false; if (fetched->type != json_string) return false; *value = fetched->u.string.ptr; return true; } bool fetch_int(json_value* object, char* name, size_t* value) { json_value* fetched; if (!fetch(object, name, &fetched)) return false; if (fetched->type != json_integer) return false; *value = (size_t)fetched->u.integer; return true; } bool fetch_bool(json_value* object, char* name, bool* value) { json_value* fetched; if (!fetch(object, name, &fetched)) return false; if (fetched->type != json_boolean) return false; *value = fetched->u.boolean; return true; } void free_patches(patches_t* patches) { if (patches->nopatchsets == 0) return; for (int i = patches->nopatchsets - 1; i >= 0; i--) { if (patches->patchsets[i] != NULL) { for (size_t j = 0; j < patches->patchsets[i]->nopatches; j++) { if (patches->patchsets[i]->patches[j].from != NULL) free(patches->patchsets[i]->patches[j].from); if (patches->patchsets[i]->patches[j].to != NULL) free(patches->patchsets[i]->patches[j].to); } free(patches->patchsets[i]); } } patches->nopatchsets = 0; } json_value* load_json_from_file(char* path, char* error) { FILE* fp; fopen_s(&fp, path, "r"); if (fp == NULL) { if (error != NULL) snprintf(error, json_error_max, "Failed to open patch file"); return NULL; } fseek(fp, 0L, SEEK_END); size_t sz = ftell(fp); rewind(fp); char* json_buf = (char*)malloc(sz); if (json_buf == NULL) { if (error != NULL) snprintf(error, json_error_max, "Failed to allocate file buffer"); fclose(fp); return NULL; } if (!(sz = fread(json_buf, 1, sz, fp))) { if (error != NULL) snprintf(error, json_error_max, "Failed to read file"); fclose(fp); return NULL; }; fclose(fp); json_settings settings = { 0 }; return json_parse_ex(&settings, json_buf, sz, error); } bool parse_patches(patches_t* patches, json_value** set_json, int set_count, char* error) { patches->nopatchsets = set_count; patches->patchsets = (patchset_t**)malloc(set_count * sizeof(patchset_t*)); for (int i = 0; i < set_count; i++) patches->patchsets[i] = NULL; error[0] = '\0'; for (int i = 0; i < set_count; i++) { patchset_t* patchset = (patchset_t*)malloc(sizeof(patchset_t)); patches->patchsets[i] = patchset; patchset->name = NULL; patchset->description = NULL; patchset->binary_name = NULL; patchset->apply = false; patchset->nopatches = 0; char *name, *description, *binary_name; if (!fetch_string(set_json[i], "name", &name)) { snprintf(error, json_error_max, "'name' missing for patch %d", i); goto failed; } if (!fetch_string(set_json[i], "description", &description)) { snprintf(error, json_error_max, "'description' missing for patch %d (%s)", i, name); goto failed; } if (!fetch_string(set_json[i], "binary_name", &binary_name)) binary_name = NULL; bool apply; if (!fetch_bool(set_json[i], "apply", &apply)) { snprintf(error, json_error_max, "'apply' missing for patch %d (%s)", i, name); goto failed; } json_value* set_patches; if (!fetch(set_json[i], "patches", &set_patches) || set_patches->type != json_array) { char* patches_file_path; if (fetch_string(set_json[i], "patches_file", &patches_file_path)) { char load_error[json_error_max]; set_patches = load_json_from_file(patches_file_path, load_error); if (set_patches == NULL) { fprintf(stderr, "W: patcher: Failed to load '%s': %s\n", patches_file_path, load_error); continue; } if (set_patches->type != json_array) { fprintf(stderr, "W: patcher: Failed to load '%s': not an array\n", patches_file_path); continue; } } else { snprintf(error, json_error_max, "Neither 'patches' nor 'patchs_file' in patch %d (%s)", i, name); goto failed; } } int count = set_patches->u.array.length; patchset = (patchset_t*)realloc(patchset, sizeof(patchset_t) + (sizeof(patch_t) * count)); patches->patchsets[i] = patchset; patchset->name = name; patchset->description = description; patchset->binary_name = binary_name; patchset->apply = apply; patchset->nopatches = count; memset(patchset->patches, 0, count * (sizeof(patch_t))); for (int j = 0; j < count; j++) { json_value* this_patch = set_patches->u.array.values[j]; size_t at; char *from, *to; if (!fetch_int(this_patch, "at", &at)) { char* at_str; if (!fetch_string(this_patch, "at", &at_str)) { snprintf(error, json_error_max, "'at' missing for patch %s[%d]", name, j); goto failed; } if (strlen(at_str) > 8) { snprintf(error, json_error_max, "invalid 'at' value for patch %s[%d]", name, j); goto failed; } hex_to_bin(at_str, (void*)&at, 8); at = _byteswap_ulong(at); // Endianess! } if (!fetch_string(this_patch, "from", &from)) { snprintf(error, json_error_max, "'from' missing for patch %s[%d]", name, j); goto failed; } if (!fetch_string(this_patch, "to", &to)) { snprintf(error, json_error_max, "'to' missing for patch %s[%d]", name, j); goto failed; } char* patch_name; if (!fetch_string(this_patch, "name", &patch_name)) patch_name = NULL; patchset->patches[j].name = patch_name; patchset->patches[j].offset = at; size_t size = strlen(from); if (size != strlen(to)) { snprintf(error, json_error_max, "'from' and 'to' lengths differ in patch %s[%d]", name, j); goto failed; } if (size % 2 != 0) { snprintf(error, json_error_max, "invalid hex string in patch %s[%d]", name, j); goto failed; } unsigned char* bin_from = patchset->patches[j].from = (unsigned char*)malloc(size / 2); unsigned char* bin_to = patchset->patches[j].to = (unsigned char*)malloc(size / 2); if (!hex_to_bin(from, bin_from, size)) { snprintf(error, json_error_max, "invalid hex string in patch %s[%d].from", name, j); goto failed; }; if (!hex_to_bin(to, bin_to, size)) { snprintf(error, json_error_max, "invalid hex string in patch %s[%d].to", name, j); goto failed; }; patchset->patches[j].count = size / 2; } } return true; failed: free_patches(patches); return false; } bool load_patches(patches_t* patches, char* path, char* error) { patches->nopatchsets = 0; json_value* parsed = load_json_from_file(path, error); if (parsed == NULL) return false; int loaded_patches = 0; json_value** patches_json; if (parsed->type == json_array) { loaded_patches = parsed->u.array.length; patches_json = parsed->u.array.values; } else if (parsed->type == json_object) { loaded_patches = 1; patches_json = &parsed; } else { snprintf(error, json_error_max, "Patch file format error"); json_value_free(parsed); return false; } if (!parse_patches(patches, patches_json, loaded_patches, error)) return false; return true; }