mirror of
https://github.com/spice2x/spice2x.github.io.git
synced 2026-08-02 14:50:41 -07:00
be4bb00390
## Link to GitHub Issue or related Pull Request, if one exists Fixes #245 ## Description of change Introduce patch groups. JSON schema now allows for a `"type": "group"`: ```json { "type": "group", "gameCode": "LDJ", "id": "timer-freeze", "name": "Timer Freeze Patches", "description": "Freezes various timers in the game." }, { "name": "Standard/Menu Timer Freeze", "description": "Freezes all non-premium area timers.", "caution": "", "gameCode": "LDJ", "type": "memory", "group": "timer-freeze", "patches": [ { "offset": 9962743, "dllName": "bm2dx.dll", "dataDisabled": "0F84", "dataEnabled": "90E9" } ] }, { "name": "Premium Free Timer Freeze", "description": "Freezes all premium area timers.", "caution": "", "gameCode": "LDJ", "type": "memory", "group": "timer-freeze", "patches": [ { "offset": 9111965, "dllName": "bm2dx.dll", "dataDisabled": "7E", "dataEnabled": "EB" } ] }, ``` In the UI, these will show up as tree nodes. The parent is not actually treated as a patch; e.g., its state is not saved to the patch manager config file; only the child patches states are managed, the parent's state only bubble up only in the UI. In downlevel versions of spice, group parents will show up as a patch of invalid type. Child patches will still show up as individual patches. ## Testing *how was the code tested?*
143 lines
4.0 KiB
C++
143 lines
4.0 KiB
C++
#pragma once
|
|
|
|
#include <filesystem>
|
|
#include <functional>
|
|
#include <memory>
|
|
#include <optional>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
namespace patcher {
|
|
|
|
enum class PatchType {
|
|
Unknown,
|
|
Memory,
|
|
Signature,
|
|
Union,
|
|
Integer,
|
|
};
|
|
|
|
enum class PatchStatus {
|
|
Error,
|
|
Disabled,
|
|
Enabled,
|
|
};
|
|
|
|
enum class PatchUrlStatus {
|
|
Valid,
|
|
Invalid,
|
|
Unapplied,
|
|
ValidButNoData,
|
|
Partial,
|
|
};
|
|
|
|
struct MemoryPatch {
|
|
std::string dll_name = "";
|
|
std::shared_ptr<uint8_t[]> data_disabled = nullptr;
|
|
size_t data_disabled_len = 0;
|
|
std::shared_ptr<uint8_t[]> data_enabled = nullptr;
|
|
size_t data_enabled_len = 0;
|
|
uint64_t data_offset = 0;
|
|
uint8_t *data_offset_ptr = nullptr;
|
|
bool fatal_error = false;
|
|
};
|
|
|
|
struct PatchData;
|
|
struct SignaturePatch {
|
|
std::string dll_name = "";
|
|
std::string signature = "", replacement = "";
|
|
uint64_t offset = 0;
|
|
int64_t usage = 0;
|
|
|
|
MemoryPatch to_memory(PatchData *patch);
|
|
};
|
|
|
|
struct UnionPatch {
|
|
std::string name = "";
|
|
std::string dll_name = "";
|
|
std::shared_ptr<uint8_t[]> data = nullptr;
|
|
size_t data_len = 0;
|
|
uint64_t offset = 0;
|
|
uint8_t *data_offset_ptr = nullptr;
|
|
bool fatal_error = false;
|
|
};
|
|
|
|
struct NumberPatch {
|
|
std::string dll_name = "";
|
|
uint64_t data_offset = 0;
|
|
uint8_t *data_offset_ptr = nullptr;
|
|
int32_t min;
|
|
int32_t max;
|
|
int32_t value;
|
|
size_t size_in_bytes;
|
|
bool fatal_error = false;
|
|
};
|
|
|
|
struct PatchGroup {
|
|
std::string name;
|
|
std::string description;
|
|
std::string caution;
|
|
std::string name_in_lower_case;
|
|
};
|
|
|
|
struct PatchData {
|
|
bool enabled;
|
|
std::string game_code;
|
|
int datecode_min = 0;
|
|
int datecode_max = 0;
|
|
std::string name, description, caution;
|
|
std::string name_in_lower_case = "";
|
|
PatchType type;
|
|
bool preset;
|
|
std::vector<MemoryPatch> patches_memory;
|
|
std::vector<UnionPatch> patches_union;
|
|
NumberPatch patch_number;
|
|
std::string group_id;
|
|
PatchStatus last_status;
|
|
std::string hash;
|
|
bool unverified = false;
|
|
std::string peIdentifier;
|
|
std::string error_reason = "";
|
|
|
|
// for union patch only
|
|
std::string selected_union_name = "";
|
|
};
|
|
|
|
extern std::optional<std::string> PATCH_MANAGER_CFG_PATH_OVERRIDE;
|
|
|
|
std::string get_game_identifier(const std::filesystem::path& dll_path, bool print_info = false);
|
|
|
|
// lifecycle: init() sets up the config path and registers for DLL-load
|
|
// notifications; apply_patches_on_start() applies auto-apply patches once the
|
|
// game DLL is loaded. both are safe to call repeatedly.
|
|
void init();
|
|
void apply_patches_on_start();
|
|
|
|
// patch load / apply operations (also driven by the overlay window)
|
|
void reload_local_patches(bool apply_patches = false);
|
|
bool import_remote_patches_to_disk();
|
|
void hard_apply_patches();
|
|
void config_load();
|
|
void config_save();
|
|
std::vector<std::string> getExtraDlls(const std::string& firstDll);
|
|
|
|
// process-wide patch engine state, rendered and edited by the overlay window
|
|
extern std::filesystem::path config_path;
|
|
extern bool config_dirty;
|
|
extern bool setting_auto_apply;
|
|
extern std::string patch_url;
|
|
extern std::string ACTIVE_JSON_FILE;
|
|
extern std::vector<PatchData> patches;
|
|
extern bool local_patches_initialized;
|
|
extern std::vector<size_t> patches_sorted;
|
|
extern std::string url_fetch_errors;
|
|
extern size_t url_recent_idx;
|
|
extern std::vector<std::string> url_recents;
|
|
|
|
PatchStatus is_patch_active(PatchData& patch);
|
|
bool apply_patch(PatchData& patch, bool active);
|
|
const PatchGroup* find_patch_group(const PatchData& patch);
|
|
|
|
std::string displayPath(const std::filesystem::path& path);
|
|
}
|