Recaf icon indicating copy to clipboard operation
Recaf copied to clipboard

[SUGGESTION] Add an "Export as script / patch" option to the Modifications window

Open cranberry3148 opened this issue 3 years ago • 2 comments

I was thinking of this for a while, and it would be a pretty cool feature to easily and automatically patch a jar file that, for example, got a minor update with your previous patches.

cranberry3148 avatar Sep 07 '22 15:09 cranberry3148

I made this script for somebody a while ago but it can be a sorta-nice hold-hover:

// ==Metadata==
// @name Difference loader
// @description Loads another file to apply on top of the primary resource
// @version 1.0
// @author Matt Coley
// ==/Metadata==
import me.coley.recaf.code.ClassInfo;
import me.coley.recaf.code.FileInfo;
import me.coley.recaf.scripting.impl.DialogAPI;
import me.coley.recaf.scripting.impl.WorkspaceAPI;
import me.coley.recaf.util.logging.Logging;
import me.coley.recaf.workspace.resource.ClassMap;
import me.coley.recaf.workspace.resource.FileMap;
import me.coley.recaf.workspace.resource.Resource;
import me.coley.recaf.workspace.resource.ResourceIO;
import org.slf4j.Logger;

import java.io.File;
import java.io.IOException;
import java.util.Arrays;

// Check current workspace is open
Logger log = Logging.get("CompareScript");
Resource primary = WorkspaceAPI.getPrimaryResource();
if (primary == null) {
	log.warn("A workspace needs to be open first!");
	return;
}
// Prompt and open a resource (jar/etc)
Resource resource;
try {
	File file = DialogAPI.singleOpenFileDialog();
	resource = ResourceIO.fromPath(file.toPath(), true);
} catch (IOException ex) {
	log.error("Failed to read contents from selected file", ex);
	return;
}
// Copy classes/files from the new resource into the primary resource
int[] modified = { 0, 0 };
ClassMap classes = primary.getClasses();
FileMap files = primary.getFiles();
for (ClassInfo newInfo : resource.getClasses().values()) {
	ClassInfo oldInfo = classes.get(newInfo.getName());
	if (oldInfo == null || !Arrays.equals(oldInfo.getValue(), newInfo.getValue())) {
		classes.put(newInfo);
		modified[0]++;
	}
}
for (FileInfo newInfo : resource.getFiles().values()) {
	FileInfo oldInfo = files.get(newInfo.getName());
	if (oldInfo == null || !Arrays.equals(oldInfo.getValue(), newInfo.getValue())) {
		files.put(newInfo);
		modified[1]++;
	}
}
log.info("Updated {} classes, {} files", modified[0], modified[1]);

Keep a jar with just the modified classes then you can use this script. Its not great but its the closest we got until a proper patch system is implemented.

Col-E avatar Sep 07 '22 22:09 Col-E

Draft idea/model:

  • Text diff (similar to git)
  • Basic bin-diff (byte[] vs byte[], insertion/removals at offsets)
  • Class diff (comprised of method and field diffs)
  • Field diff
    • key: name / type
  • Method diff
    • key: name / type
    • Can be text-diff of disassembly, or bytecode (asm) diff similar to how Mixins work with @At(target)

Col-E avatar Sep 21 '22 13:09 Col-E

https://github.com/Col-E/Recaf/commit/817aa24da25565a610479cdcda038a3210bd602b - A more proper patch-like system has been added to the 4.X API - UI pending

Col-E avatar Jul 25 '24 11:07 Col-E