From 7fb5a18a4262d968456b74407bff8e120d4ccf62 Mon Sep 17 00:00:00 2001 From: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Date: Fri, 16 Jan 2026 18:58:24 +0100 Subject: [PATCH 1/2] feat: Localized patch/option name, titles, descriptions --- api/morphe-patcher.api | 72 ++++++- .../kotlin/app/morphe/patcher/patch/Option.kt | 122 ++++++++--- .../kotlin/app/morphe/patcher/patch/Patch.kt | 195 ++++++++++++++++-- .../morphe/patcher/patch/PatchLocalization.kt | 48 +++++ 4 files changed, 380 insertions(+), 57 deletions(-) create mode 100644 src/main/kotlin/app/morphe/patcher/patch/PatchLocalization.kt diff --git a/api/morphe-patcher.api b/api/morphe-patcher.api index 25d18089..50b48195 100644 --- a/api/morphe-patcher.api +++ b/api/morphe-patcher.api @@ -368,6 +368,7 @@ public final class app/morphe/patcher/patch/BytecodePatch : app/morphe/patcher/p } public final class app/morphe/patcher/patch/BytecodePatchBuilder : app/morphe/patcher/patch/PatchBuilder { + public fun (Ljava/lang/String;Ljava/lang/String;Z)V public synthetic fun build$morphe_patcher ()Lapp/morphe/patcher/patch/Patch; public final fun extendWith (Ljava/lang/String;)Lapp/morphe/patcher/patch/BytecodePatchBuilder; public final fun getExtensionInputStream ()Ljava/util/function/Supplier; @@ -392,21 +393,24 @@ public final class app/morphe/patcher/patch/BytecodePatchContext : app/morphe/pa } public final class app/morphe/patcher/patch/Option { - public fun (Ljava/lang/String;Ljava/lang/Object;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/reflect/KType;Lkotlin/jvm/functions/Function2;)V - public synthetic fun (Ljava/lang/String;Ljava/lang/Object;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/reflect/KType;Lkotlin/jvm/functions/Function2;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public fun (Ljava/lang/String;Ljava/lang/Object;Ljava/util/Map;Ljava/lang/String;ZLkotlin/reflect/KType;Lkotlin/jvm/functions/Function2;)V - public synthetic fun (Ljava/lang/String;Ljava/lang/Object;Ljava/util/Map;Ljava/lang/String;ZLkotlin/reflect/KType;Lkotlin/jvm/functions/Function2;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Ljava/lang/String;Ljava/lang/Object;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLkotlin/reflect/KType;Lkotlin/jvm/functions/Function2;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/Object;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLkotlin/reflect/KType;Lkotlin/jvm/functions/Function2;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun getDefault ()Ljava/lang/Object; public final fun getDescription ()Ljava/lang/String; + public final fun getDescriptionKey ()Ljava/lang/String; + public final fun getDescriptionLocalized ()Ljava/lang/String; public final fun getKey ()Ljava/lang/String; public final fun getName ()Ljava/lang/String; public final fun getRequired ()Z public final fun getTitle ()Ljava/lang/String; + public final fun getTitleKey ()Ljava/lang/String; + public final fun getTitleLocalized ()Ljava/lang/String; public final fun getType ()Lkotlin/reflect/KType; public final fun getValidator ()Lkotlin/jvm/functions/Function2; public final fun getValue ()Ljava/lang/Object; public final fun getValue (Ljava/lang/Object;Lkotlin/reflect/KProperty;)Ljava/lang/Object; public final fun getValues ()Ljava/util/Map; + public final fun getValuesLocalized ()Ljava/util/Map; public final fun reset ()V public final fun setValue (Ljava/lang/Object;)V public final fun setValue (Ljava/lang/Object;Lkotlin/reflect/KProperty;Ljava/lang/Object;)V @@ -434,43 +438,81 @@ public final class app/morphe/patcher/patch/OptionException$ValueValidationExcep } public final class app/morphe/patcher/patch/OptionKt { + public static final fun booleanOption (Lapp/morphe/patcher/patch/PatchBuilder;Ljava/lang/String;Ljava/lang/Boolean;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lapp/morphe/patcher/patch/Option; public static final fun booleanOption (Lapp/morphe/patcher/patch/PatchBuilder;Ljava/lang/String;Ljava/lang/Boolean;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lapp/morphe/patcher/patch/Option; + public static final fun booleanOption (Ljava/lang/String;Ljava/lang/Boolean;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lapp/morphe/patcher/patch/Option; public static final fun booleanOption (Ljava/lang/String;Ljava/lang/Boolean;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lapp/morphe/patcher/patch/Option; + public static synthetic fun booleanOption$default (Lapp/morphe/patcher/patch/PatchBuilder;Ljava/lang/String;Ljava/lang/Boolean;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/morphe/patcher/patch/Option; public static synthetic fun booleanOption$default (Lapp/morphe/patcher/patch/PatchBuilder;Ljava/lang/String;Ljava/lang/Boolean;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/morphe/patcher/patch/Option; + public static synthetic fun booleanOption$default (Ljava/lang/String;Ljava/lang/Boolean;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/morphe/patcher/patch/Option; public static synthetic fun booleanOption$default (Ljava/lang/String;Ljava/lang/Boolean;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/morphe/patcher/patch/Option; + public static final fun booleansOption (Lapp/morphe/patcher/patch/PatchBuilder;Ljava/lang/String;Ljava/util/List;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lapp/morphe/patcher/patch/Option; public static final fun booleansOption (Lapp/morphe/patcher/patch/PatchBuilder;Ljava/lang/String;Ljava/util/List;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lapp/morphe/patcher/patch/Option; + public static final fun booleansOption (Ljava/lang/String;Ljava/util/List;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lapp/morphe/patcher/patch/Option; public static final fun booleansOption (Ljava/lang/String;Ljava/util/List;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lapp/morphe/patcher/patch/Option; + public static synthetic fun booleansOption$default (Lapp/morphe/patcher/patch/PatchBuilder;Ljava/lang/String;Ljava/util/List;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/morphe/patcher/patch/Option; public static synthetic fun booleansOption$default (Lapp/morphe/patcher/patch/PatchBuilder;Ljava/lang/String;Ljava/util/List;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/morphe/patcher/patch/Option; + public static synthetic fun booleansOption$default (Ljava/lang/String;Ljava/util/List;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/morphe/patcher/patch/Option; public static synthetic fun booleansOption$default (Ljava/lang/String;Ljava/util/List;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/morphe/patcher/patch/Option; + public static final fun floatOption (Lapp/morphe/patcher/patch/PatchBuilder;Ljava/lang/String;Ljava/lang/Float;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lapp/morphe/patcher/patch/Option; public static final fun floatOption (Lapp/morphe/patcher/patch/PatchBuilder;Ljava/lang/String;Ljava/lang/Float;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lapp/morphe/patcher/patch/Option; + public static final fun floatOption (Ljava/lang/String;Ljava/lang/Float;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lapp/morphe/patcher/patch/Option; public static final fun floatOption (Ljava/lang/String;Ljava/lang/Float;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lapp/morphe/patcher/patch/Option; + public static synthetic fun floatOption$default (Lapp/morphe/patcher/patch/PatchBuilder;Ljava/lang/String;Ljava/lang/Float;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/morphe/patcher/patch/Option; public static synthetic fun floatOption$default (Lapp/morphe/patcher/patch/PatchBuilder;Ljava/lang/String;Ljava/lang/Float;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/morphe/patcher/patch/Option; + public static synthetic fun floatOption$default (Ljava/lang/String;Ljava/lang/Float;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/morphe/patcher/patch/Option; public static synthetic fun floatOption$default (Ljava/lang/String;Ljava/lang/Float;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/morphe/patcher/patch/Option; + public static final fun floatsOption (Lapp/morphe/patcher/patch/PatchBuilder;Ljava/lang/String;Ljava/util/List;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lapp/morphe/patcher/patch/Option; public static final fun floatsOption (Lapp/morphe/patcher/patch/PatchBuilder;Ljava/lang/String;Ljava/util/List;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lapp/morphe/patcher/patch/Option; + public static synthetic fun floatsOption$default (Lapp/morphe/patcher/patch/PatchBuilder;Ljava/lang/String;Ljava/util/List;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/morphe/patcher/patch/Option; public static synthetic fun floatsOption$default (Lapp/morphe/patcher/patch/PatchBuilder;Ljava/lang/String;Ljava/util/List;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/morphe/patcher/patch/Option; + public static final fun intOption (Lapp/morphe/patcher/patch/PatchBuilder;Ljava/lang/String;Ljava/lang/Integer;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lapp/morphe/patcher/patch/Option; public static final fun intOption (Lapp/morphe/patcher/patch/PatchBuilder;Ljava/lang/String;Ljava/lang/Integer;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lapp/morphe/patcher/patch/Option; + public static final fun intOption (Ljava/lang/String;Ljava/lang/Integer;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lapp/morphe/patcher/patch/Option; public static final fun intOption (Ljava/lang/String;Ljava/lang/Integer;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lapp/morphe/patcher/patch/Option; + public static synthetic fun intOption$default (Lapp/morphe/patcher/patch/PatchBuilder;Ljava/lang/String;Ljava/lang/Integer;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/morphe/patcher/patch/Option; public static synthetic fun intOption$default (Lapp/morphe/patcher/patch/PatchBuilder;Ljava/lang/String;Ljava/lang/Integer;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/morphe/patcher/patch/Option; + public static synthetic fun intOption$default (Ljava/lang/String;Ljava/lang/Integer;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/morphe/patcher/patch/Option; public static synthetic fun intOption$default (Ljava/lang/String;Ljava/lang/Integer;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/morphe/patcher/patch/Option; + public static final fun intsOption (Lapp/morphe/patcher/patch/PatchBuilder;Ljava/lang/String;Ljava/util/List;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lapp/morphe/patcher/patch/Option; public static final fun intsOption (Lapp/morphe/patcher/patch/PatchBuilder;Ljava/lang/String;Ljava/util/List;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lapp/morphe/patcher/patch/Option; + public static final fun intsOption (Ljava/lang/String;Ljava/util/List;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lapp/morphe/patcher/patch/Option; public static final fun intsOption (Ljava/lang/String;Ljava/util/List;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lapp/morphe/patcher/patch/Option; + public static synthetic fun intsOption$default (Lapp/morphe/patcher/patch/PatchBuilder;Ljava/lang/String;Ljava/util/List;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/morphe/patcher/patch/Option; public static synthetic fun intsOption$default (Lapp/morphe/patcher/patch/PatchBuilder;Ljava/lang/String;Ljava/util/List;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/morphe/patcher/patch/Option; + public static synthetic fun intsOption$default (Ljava/lang/String;Ljava/util/List;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/morphe/patcher/patch/Option; public static synthetic fun intsOption$default (Ljava/lang/String;Ljava/util/List;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/morphe/patcher/patch/Option; + public static final fun longOption (Lapp/morphe/patcher/patch/PatchBuilder;Ljava/lang/String;Ljava/lang/Long;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lapp/morphe/patcher/patch/Option; public static final fun longOption (Lapp/morphe/patcher/patch/PatchBuilder;Ljava/lang/String;Ljava/lang/Long;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lapp/morphe/patcher/patch/Option; + public static final fun longOption (Ljava/lang/String;Ljava/lang/Long;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lapp/morphe/patcher/patch/Option; public static final fun longOption (Ljava/lang/String;Ljava/lang/Long;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lapp/morphe/patcher/patch/Option; + public static synthetic fun longOption$default (Lapp/morphe/patcher/patch/PatchBuilder;Ljava/lang/String;Ljava/lang/Long;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/morphe/patcher/patch/Option; public static synthetic fun longOption$default (Lapp/morphe/patcher/patch/PatchBuilder;Ljava/lang/String;Ljava/lang/Long;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/morphe/patcher/patch/Option; + public static synthetic fun longOption$default (Ljava/lang/String;Ljava/lang/Long;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/morphe/patcher/patch/Option; public static synthetic fun longOption$default (Ljava/lang/String;Ljava/lang/Long;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/morphe/patcher/patch/Option; + public static final fun longsOption (Lapp/morphe/patcher/patch/PatchBuilder;Ljava/lang/String;Ljava/util/List;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lapp/morphe/patcher/patch/Option; public static final fun longsOption (Lapp/morphe/patcher/patch/PatchBuilder;Ljava/lang/String;Ljava/util/List;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lapp/morphe/patcher/patch/Option; + public static final fun longsOption (Ljava/lang/String;Ljava/util/List;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lapp/morphe/patcher/patch/Option; public static final fun longsOption (Ljava/lang/String;Ljava/util/List;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lapp/morphe/patcher/patch/Option; + public static synthetic fun longsOption$default (Lapp/morphe/patcher/patch/PatchBuilder;Ljava/lang/String;Ljava/util/List;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/morphe/patcher/patch/Option; public static synthetic fun longsOption$default (Lapp/morphe/patcher/patch/PatchBuilder;Ljava/lang/String;Ljava/util/List;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/morphe/patcher/patch/Option; + public static synthetic fun longsOption$default (Ljava/lang/String;Ljava/util/List;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/morphe/patcher/patch/Option; public static synthetic fun longsOption$default (Ljava/lang/String;Ljava/util/List;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/morphe/patcher/patch/Option; + public static final fun stringOption (Lapp/morphe/patcher/patch/PatchBuilder;Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lapp/morphe/patcher/patch/Option; public static final fun stringOption (Lapp/morphe/patcher/patch/PatchBuilder;Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lapp/morphe/patcher/patch/Option; + public static final fun stringOption (Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lapp/morphe/patcher/patch/Option; public static final fun stringOption (Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lapp/morphe/patcher/patch/Option; + public static synthetic fun stringOption$default (Lapp/morphe/patcher/patch/PatchBuilder;Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/morphe/patcher/patch/Option; public static synthetic fun stringOption$default (Lapp/morphe/patcher/patch/PatchBuilder;Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/morphe/patcher/patch/Option; + public static synthetic fun stringOption$default (Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/morphe/patcher/patch/Option; public static synthetic fun stringOption$default (Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/morphe/patcher/patch/Option; + public static final fun stringsOption (Lapp/morphe/patcher/patch/PatchBuilder;Ljava/lang/String;Ljava/util/List;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lapp/morphe/patcher/patch/Option; public static final fun stringsOption (Lapp/morphe/patcher/patch/PatchBuilder;Ljava/lang/String;Ljava/util/List;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lapp/morphe/patcher/patch/Option; + public static final fun stringsOption (Ljava/lang/String;Ljava/util/List;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lapp/morphe/patcher/patch/Option; public static final fun stringsOption (Ljava/lang/String;Ljava/util/List;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lapp/morphe/patcher/patch/Option; + public static synthetic fun stringsOption$default (Lapp/morphe/patcher/patch/PatchBuilder;Ljava/lang/String;Ljava/util/List;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/morphe/patcher/patch/Option; public static synthetic fun stringsOption$default (Lapp/morphe/patcher/patch/PatchBuilder;Ljava/lang/String;Ljava/util/List;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/morphe/patcher/patch/Option; + public static synthetic fun stringsOption$default (Ljava/lang/String;Ljava/util/List;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/morphe/patcher/patch/Option; public static synthetic fun stringsOption$default (Ljava/lang/String;Ljava/util/List;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/morphe/patcher/patch/Option; } @@ -517,20 +559,25 @@ public final class app/morphe/patcher/patch/Options : java/util/Map, kotlin/jvm/ } public abstract class app/morphe/patcher/patch/Patch { + public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLjava/util/Set;Ljava/util/Set;Ljava/util/Set;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/internal/DefaultConstructorMarker;)V public synthetic fun (Ljava/lang/String;Ljava/lang/String;ZLjava/util/Set;Ljava/util/Set;Ljava/util/Set;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun execute (Lapp/morphe/patcher/patch/PatchContext;)V public final fun finalize (Lapp/morphe/patcher/patch/PatchContext;)V public final fun getCompatiblePackages ()Ljava/util/Set; public final fun getDependencies ()Ljava/util/Set; public final fun getDescription ()Ljava/lang/String; + public final fun getDescriptionKey ()Ljava/lang/String; + public final fun getDescriptionLocalized ()Ljava/lang/String; public final fun getName ()Ljava/lang/String; + public final fun getNameKey ()Ljava/lang/String; + public final fun getNameLocalized ()Ljava/lang/String; public final fun getOptions ()Lapp/morphe/patcher/patch/Options; public final fun getUse ()Z public fun toString ()Ljava/lang/String; } public abstract class app/morphe/patcher/patch/PatchBuilder { - public synthetic fun (Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/internal/DefaultConstructorMarker;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun compatibleWith ([Ljava/lang/String;)V public final fun compatibleWith ([Lkotlin/Pair;)V public final fun dependsOn ([Lapp/morphe/patcher/patch/Patch;)V @@ -539,9 +586,11 @@ public abstract class app/morphe/patcher/patch/PatchBuilder { protected final fun getCompatiblePackages ()Ljava/util/Set; protected final fun getDependencies ()Ljava/util/Set; protected final fun getDescription ()Ljava/lang/String; + protected final fun getDescriptionKey ()Ljava/lang/String; protected final fun getExecutionBlock ()Lkotlin/jvm/functions/Function1; protected final fun getFinalizeBlock ()Lkotlin/jvm/functions/Function1; protected final fun getName ()Ljava/lang/String; + protected final fun getNameKey ()Ljava/lang/String; protected final fun getOptions ()Ljava/util/Set; protected final fun getUse ()Z public final fun invoke (Lapp/morphe/patcher/patch/Option;)Lapp/morphe/patcher/patch/Option; @@ -562,14 +611,20 @@ public final class app/morphe/patcher/patch/PatchException : java/lang/Exception } public final class app/morphe/patcher/patch/PatchKt { + public static final fun bytecodePatch (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;)Lapp/morphe/patcher/patch/BytecodePatch; public static final fun bytecodePatch (Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;)Lapp/morphe/patcher/patch/BytecodePatch; + public static synthetic fun bytecodePatch$default (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/morphe/patcher/patch/BytecodePatch; public static synthetic fun bytecodePatch$default (Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/morphe/patcher/patch/BytecodePatch; public static final fun loadPatchesFromDex (Ljava/util/Set;Ljava/io/File;)Lapp/morphe/patcher/patch/PatchLoader$Dex; public static synthetic fun loadPatchesFromDex$default (Ljava/util/Set;Ljava/io/File;ILjava/lang/Object;)Lapp/morphe/patcher/patch/PatchLoader$Dex; public static final fun loadPatchesFromJar (Ljava/util/Set;)Lapp/morphe/patcher/patch/PatchLoader$Jar; + public static final fun rawResourcePatch (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;)Lapp/morphe/patcher/patch/RawResourcePatch; public static final fun rawResourcePatch (Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;)Lapp/morphe/patcher/patch/RawResourcePatch; + public static synthetic fun rawResourcePatch$default (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/morphe/patcher/patch/RawResourcePatch; public static synthetic fun rawResourcePatch$default (Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/morphe/patcher/patch/RawResourcePatch; + public static final fun resourcePatch (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;)Lapp/morphe/patcher/patch/ResourcePatch; public static final fun resourcePatch (Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;)Lapp/morphe/patcher/patch/ResourcePatch; + public static synthetic fun resourcePatch$default (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/morphe/patcher/patch/ResourcePatch; public static synthetic fun resourcePatch$default (Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/morphe/patcher/patch/ResourcePatch; } @@ -604,6 +659,11 @@ public final class app/morphe/patcher/patch/PatchLoader$Jar : app/morphe/patcher public fun (Ljava/util/Set;)V } +public final class app/morphe/patcher/patch/PatchLocalization { + public static final field INSTANCE Lapp/morphe/patcher/patch/PatchLocalization; + public final fun setLocale (Ljava/util/Locale;)V +} + public final class app/morphe/patcher/patch/PatchResult { public final fun getException ()Lapp/morphe/patcher/patch/PatchException; public final fun getPatch ()Lapp/morphe/patcher/patch/Patch; @@ -614,6 +674,7 @@ public final class app/morphe/patcher/patch/RawResourcePatch : app/morphe/patche } public final class app/morphe/patcher/patch/RawResourcePatchBuilder : app/morphe/patcher/patch/PatchBuilder { + public fun (Ljava/lang/String;Ljava/lang/String;Z)V public synthetic fun build$morphe_patcher ()Lapp/morphe/patcher/patch/Patch; } @@ -622,6 +683,7 @@ public final class app/morphe/patcher/patch/ResourcePatch : app/morphe/patcher/p } public final class app/morphe/patcher/patch/ResourcePatchBuilder : app/morphe/patcher/patch/PatchBuilder { + public fun (Ljava/lang/String;Ljava/lang/String;Z)V public synthetic fun build$morphe_patcher ()Lapp/morphe/patcher/patch/Patch; } diff --git a/src/main/kotlin/app/morphe/patcher/patch/Option.kt b/src/main/kotlin/app/morphe/patcher/patch/Option.kt index 05490713..290489c9 100644 --- a/src/main/kotlin/app/morphe/patcher/patch/Option.kt +++ b/src/main/kotlin/app/morphe/patcher/patch/Option.kt @@ -1,5 +1,6 @@ package app.morphe.patcher.patch +import app.morphe.patcher.patch.Patch.Companion.sanitizeStringToPropertyKey import kotlin.reflect.KProperty import kotlin.reflect.KType import kotlin.reflect.typeOf @@ -11,60 +12,113 @@ import kotlin.reflect.typeOf * @param key The key. * @param default The default value. * @param values Eligible option values mapped to a human-readable name. + * @param titleKey The key of the localized title. If none is provided then the automatic key of + * `${key}.title` is used if also declared in the localized patch string file. * @param title The title. + * @param descriptionKey The key of the localized title. If none is provided then the automatic key of + * ` ${key}.description` is used if also declared in the localized patch string file. * @param description A description. * @param required Whether the option is required. * @param type The type of the option value (to handle type erasure). * @param validator The function to validate the option value. - * - * @constructor Create a new [Option]. */ @Suppress("MemberVisibilityCanBePrivate", "unused") class Option @PublishedApi -@Deprecated("Use the constructor with the name instead of a key instead.") internal constructor( - @Deprecated("Use the name property instead.") val key: String, val default: T? = null, val values: Map? = null, - @Deprecated("Use the name property instead.") + val titleKey: String? = null, val title: String? = null, + val descriptionKey: String? = null, val description: String? = null, val required: Boolean = false, val type: KType, val validator: Option.(T?) -> Boolean = { true }, -) -{ +) { + @Deprecated("Use the key property instead.") + val name = key + /** - * The name. + * Required to lookup option keys by patch name, and to lookup resources from the mpp jar. */ - val name = key + internal var enclosingPatch: Patch<*>? = null + + private fun getEnclosingPatch() : Patch<*> { + val patch = enclosingPatch + check(patch != null) { + "Option does not belong to any patch. Add the option to a patch to use it: \'$key\'" + } + return patch + } + + private fun getPatchResourceLoader() = getEnclosingPatch().resourceClassLoader + + private fun getLocalizationPropertySubKey(key: String) : String { + val parentKey = getEnclosingPatch().localizationPropertyKey ?: return key + return "$parentKey.$key" + } /** - * An option. - * - * @param T The value type of the option. - * @param name The name. - * @param default The default value. - * @param values Eligible option values mapped to a human-readable name. - * @param description A description. - * @param required Whether the option is required. - * @param type The type of the option value (to handle type erasure). - * @param validator The function to validate the option value. - * - * @constructor Create a new [Option]. + * @return The localized name using the patch string declarations. + * Falls back to non localized [key] if no localizations option name exists. */ - @PublishedApi - internal constructor( - name: String, - default: T? = null, - values: Map? = null, - description: String? = null, - required: Boolean = false, - type: KType, - validator: Option.(T?) -> Boolean = { true }, - ) : this(name, default, values, name, description, required, type, validator) + val titleLocalized: String? + get() { + // Backwards compatibility requires handling no resource loader. + val classLoader = getPatchResourceLoader() ?: return title + + val stringKey = titleKey ?: getLocalizationPropertySubKey("$key.title") + val localized = PatchLocalization.getLocalizedString( + classLoader, stringKey + ) + + return localized ?: title + } + + /** + * @return The localized description using patch string declarations. + * Falls back to non localized [description] if no localized patch description exist. + */ + val descriptionLocalized: String? + get() { + // Backwards compatibility requires handling no resource loader. + val classLoader = getPatchResourceLoader() ?: return description + val stringKey = descriptionKey ?: getLocalizationPropertySubKey("$key.description") + + val localized = PatchLocalization.getLocalizedString( + classLoader, stringKey + ) + + return localized ?: description + } + + /** + * @return The localized description using patch string declarations. + * Falls back to non localized [description] if no localized patch description exist. + */ + val valuesLocalized: Map? + get() { + if (values.isNullOrEmpty()) return values + + // Backwards compatibility requires handling no resource loader. + val classLoader = getPatchResourceLoader() ?: return values + val localizedMap = mutableMapOf() + + values.forEach { (valueKey, valueValue) -> + val stringKey = getLocalizationPropertySubKey( + "$key.values." + sanitizeStringToPropertyKey(valueKey) + ) + + val localized = PatchLocalization.getLocalizedString( + classLoader, stringKey + ) + localizedMap[localized ?: valueKey] = valueValue + } + + return localizedMap + } /** * The value of the [Option]. @@ -145,7 +199,7 @@ internal constructor( class Options internal constructor( private val options: Map>, ) : Map> by options { - internal constructor(options: Set>) : this(options.associateBy { it.name }) + internal constructor(options: Set>) : this(options.associateBy { it.key }) /** * Set an option's value. @@ -892,14 +946,14 @@ sealed class OptionException(errorMessage: String) : Exception(errorMessage, nul * * @param value The value that failed validation. */ - class ValueValidationException(value: Any?, option: Option<*>) : OptionException("The option value \"$value\" failed validation for ${option.name}") + class ValueValidationException(value: Any?, option: Option<*>) : OptionException("The option value \"$value\" failed validation for ${option.key}") /** * An exception thrown when a value is required but null was passed. * * @param option The [Option] that requires a value. */ - class ValueRequiredException(option: Option<*>) : OptionException("The option ${option.name} requires a value, but the value was null") + class ValueRequiredException(option: Option<*>) : OptionException("The option ${option.key} requires a value, but the value was null") /** * An exception thrown when a [Option] is not found. diff --git a/src/main/kotlin/app/morphe/patcher/patch/Patch.kt b/src/main/kotlin/app/morphe/patcher/patch/Patch.kt index 0d53cae7..2f3746f1 100644 --- a/src/main/kotlin/app/morphe/patcher/patch/Patch.kt +++ b/src/main/kotlin/app/morphe/patcher/patch/Patch.kt @@ -21,26 +21,25 @@ typealias VersionName = String typealias Package = Pair?> /** - * A patch. - * * @param C The [PatchContext] to execute and finalize the patch with. - * @param name The name of the patch. - * If null, the patch is named "Patch" and will not be loaded by [PatchLoader]. + * @param name The name of the patch. If null then this patch not be loaded by [PatchLoader]. + * @param nameKey Explicit localized name key for [nameLocalized]. * @param description The description of the patch. + * @param descriptionKey Explicit localized description key for [descriptionLocalized]. * @param use Weather or not the patch should be used. * @param dependencies Other patches this patch depends on. * @param compatiblePackages The packages the patch is compatible with. - * If null, the patch is compatible with all packages. + * If null, the patch is compatible with all packages. * @param options The options of the patch. * @param executeBlock The execution block of the patch. * @param finalizeBlock The finalizing block of the patch. Called after all patches have been executed, - * in reverse order of execution. - * - * @constructor Create a new patch. + * in reverse order of execution. */ sealed class Patch>( val name: String?, + val nameKey: String?, val description: String?, + val descriptionKey: String?, val use: Boolean, val dependencies: Set>, val compatiblePackages: Set?, @@ -55,6 +54,70 @@ sealed class Patch>( */ val options = Options(options) + internal val localizationPropertyKey = name?.let {sanitizeStringToPropertyKey(it)} + + /** + * Workaround to fix problems that DSL introduced. + * classloader for the patch is needed to easily lookup resources inside the patches mpp file, + * but with DSL and functions the class is always this Patch class inside the patcher jar. + * To temporarily fix this, the execute or finalize block is used since it's _usually_ declared + * and it is inside the mpp file so the correct class loader can be used. + * + * TODO: Remove the remaining DSL from patcher because: + * 1. DSL adds unnecessary complexity. + * 2. DSL introduces new problems that class based patches did not have (such as here). + * 3. DSL is only used in superficial ways of setting single fields/methods of a patch. + * 4. DSL is another layer third party devs must navigate instead of using simple class based declarations. + * 5. DSL historically has been unwanted and rejected by many third party patch devs that did not migrate to it. + * 6. DSL solves no problems, provides no tangible benefits, and it's use with patches is questionable inappropriate. + */ + internal val resourceClassLoader by lazy { + val finalize = finalizeBlock + if (finalize != null) return@lazy finalize.javaClass + + val javaClass = executeBlock.javaClass + if (javaClass.name.startsWith(PatchBuilder::class.java.name)) { + Logger.getLogger(this::class.java.name).warning( + "Patch string resource classloader cannot be found for:'$name\'. To fix this, " + + "add an empty execute or finalize block to the patch declaration." + ) + return@lazy null; + } + javaClass + } + + init { + options.forEach { option -> + option.enclosingPatch = this + } + } + + /** + * @return The localized name using the patch string declarations. + * Falls back to non localized [name] if no localized patch name exist. + */ + val nameLocalized by lazy { + val instanceClass = resourceClassLoader ?: return@lazy name + + val stringKey = nameKey ?: "$localizationPropertyKey.name" + val localized = PatchLocalization.getLocalizedString(instanceClass, stringKey) + + localized ?: name + } + + /** + * @return The localized description using patch string declarations. + * Falls back to non localized [description] if no localized patch description exist. + */ + val descriptionLocalized by lazy { + val instanceClass = resourceClassLoader ?: return@lazy description + + val stringKey = descriptionKey ?: "$localizationPropertyKey.description" + val localized = PatchLocalization.getLocalizedString(instanceClass, stringKey) + + localized ?: description + } + /** * Calls the execution block of the patch. * This function is called by [Patcher.invoke]. @@ -89,6 +152,10 @@ sealed class Patch>( override fun toString() = name ?: "Patch@${System.identityHashCode(this)}" + + internal companion object { + fun sanitizeStringToPropertyKey(text: String) = text.lowercase(Locale.ROOT).replace(' ', '_') + } } internal fun Patch<*>.anyRecursively( @@ -137,7 +204,9 @@ internal fun Iterable>.forEachRecursively( */ class BytecodePatch internal constructor( name: String?, + nameKey: String?, description: String?, + descriptionKey: String?, use: Boolean, compatiblePackages: Set?, dependencies: Set>, @@ -147,7 +216,9 @@ class BytecodePatch internal constructor( finalizeBlock: ((BytecodePatchContext) -> Unit)?, ) : Patch( name, + nameKey, description, + descriptionKey, use, dependencies, compatiblePackages, @@ -184,7 +255,9 @@ class BytecodePatch internal constructor( */ class RawResourcePatch internal constructor( name: String?, + nameKey: String?, description: String?, + descriptionKey: String?, use: Boolean, compatiblePackages: Set?, dependencies: Set>, @@ -193,7 +266,9 @@ class RawResourcePatch internal constructor( finalizeBlock: ((ResourcePatchContext) -> Unit)?, ) : Patch( name, + nameKey, description, + descriptionKey, use, dependencies, compatiblePackages, @@ -227,7 +302,9 @@ class RawResourcePatch internal constructor( */ class ResourcePatch internal constructor( name: String?, + nameKey: String?, description: String?, + descriptionKey: String?, use: Boolean, compatiblePackages: Set?, dependencies: Set>, @@ -236,7 +313,9 @@ class ResourcePatch internal constructor( finalizeBlock: ((ResourcePatchContext) -> Unit)?, ) : Patch( name, + nameKey, description, + descriptionKey, use, dependencies, compatiblePackages, @@ -271,14 +350,15 @@ class ResourcePatch internal constructor( */ sealed class PatchBuilder>( protected val name: String?, + protected val nameKey: String?, protected val description: String?, + protected val descriptionKey: String?, protected val use: Boolean, ) { protected var compatiblePackages: MutableSet? = null protected var dependencies = mutableSetOf>() protected val options = mutableSetOf>() - - protected var executionBlock: ((C) -> Unit) = { } + protected var executionBlock: ((C) -> Unit) = {} // TODO: rename to executeBlock protected var finalizeBlock: ((C) -> Unit)? = null /** @@ -370,22 +450,33 @@ sealed class PatchBuilder>( private fun > B.buildPatch(block: B.() -> Unit = {}) = apply(block).build() /** - * A [BytecodePatchBuilder] builder. - * * @param name The name of the patch. * If null, the patch is named "Patch" and will not be loaded by [PatchLoader]. * @param description The description of the patch. * @param use Weather or not the patch should be used. * @property extensionInputStream Getter for the extension input stream of the patch. * An extension is a precompiled DEX file that is merged into the patched app before this patch is executed. - * - * @constructor Create a new [BytecodePatchBuilder] builder. */ class BytecodePatchBuilder internal constructor( name: String?, + nameKey: String?, description: String?, + descriptionKey: String?, use: Boolean, -) : PatchBuilder(name, description, use) { +) : PatchBuilder( + name, + nameKey, + description, + descriptionKey, + use +) { + @Deprecated("Here only for binary backwards compatibility") + constructor( + name: String?, + description: String?, + use: Boolean, + ) : this(name, null, description, null, use) + // Must be internal for the inlined function "extendWith". @PublishedApi internal var extensionInputStream: Supplier? = null @@ -408,7 +499,9 @@ class BytecodePatchBuilder internal constructor( override fun build() = BytecodePatch( name, + nameKey, description, + descriptionKey, use, compatiblePackages, dependencies, @@ -432,10 +525,12 @@ class BytecodePatchBuilder internal constructor( */ fun bytecodePatch( name: String? = null, + nameKey: String? = null, description: String? = null, + descriptionKey: String? = null, use: Boolean = true, block: BytecodePatchBuilder.() -> Unit = {}, -) = BytecodePatchBuilder(name, description, use).buildPatch(block) as BytecodePatch +) = BytecodePatchBuilder(name, nameKey, description, descriptionKey, use).buildPatch(block) as BytecodePatch /** * A [RawResourcePatch] builder. @@ -449,12 +544,24 @@ fun bytecodePatch( */ class RawResourcePatchBuilder internal constructor( name: String?, + nameKey: String?, description: String?, + descriptionKey: String?, use: Boolean, -) : PatchBuilder(name, description, use) { +) : PatchBuilder(name, nameKey, description, descriptionKey, use) { + + //@Deprecated("Here only for binary backwards compatibility") + constructor( + name: String?, + description: String?, + use: Boolean, + ) : this(name, null, description, null, use) + override fun build() = RawResourcePatch( name, + nameKey, description, + descriptionKey, use, compatiblePackages, dependencies, @@ -474,6 +581,7 @@ class RawResourcePatchBuilder internal constructor( * @param block The block to build the patch. * @return The created [RawResourcePatch]. */ +//@Deprecated("Here only for binary backwards compatibility") fun rawResourcePatch( name: String? = null, description: String? = null, @@ -481,6 +589,25 @@ fun rawResourcePatch( block: RawResourcePatchBuilder.() -> Unit = {}, ) = RawResourcePatchBuilder(name, description, use).buildPatch(block) as RawResourcePatch +/** + * Create a new [RawResourcePatch]. + * + * @param name The name of the patch. + * If null, the patch is named "Patch" and will not be loaded by [PatchLoader]. + * @param description The description of the patch. + * @param use Weather or not the patch should be used. + * @param block The block to build the patch. + * @return The created [RawResourcePatch]. + */ +fun rawResourcePatch( + name: String? = null, + nameKey: String? = null, + description: String? = null, + descriptionKey: String? = null, + use: Boolean = true, + block: RawResourcePatchBuilder.() -> Unit = {}, +) = RawResourcePatchBuilder(name, nameKey, description, descriptionKey, use).buildPatch(block) as RawResourcePatch + /** * A [ResourcePatch] builder. * @@ -493,12 +620,23 @@ fun rawResourcePatch( */ class ResourcePatchBuilder internal constructor( name: String?, + nameKey: String?, description: String?, + descriptionKey: String?, use: Boolean, -) : PatchBuilder(name, description, use) { +) : PatchBuilder(name, nameKey, description, descriptionKey, use) { + @Deprecated("Here only for binary backwards compatibility") + constructor( + name: String?, + description: String?, + use: Boolean, + ) : this(name, null, description, null, use) + override fun build() = ResourcePatch( name, + nameKey, description, + descriptionKey, use, compatiblePackages, dependencies, @@ -519,6 +657,7 @@ class ResourcePatchBuilder internal constructor( * * @return The created [ResourcePatch]. */ +//@Deprecated("Here only for binary backwards compatibility") fun resourcePatch( name: String? = null, description: String? = null, @@ -526,6 +665,26 @@ fun resourcePatch( block: ResourcePatchBuilder.() -> Unit = {}, ) = ResourcePatchBuilder(name, description, use).buildPatch(block) as ResourcePatch +/** + * Create a new [ResourcePatch]. + * + * @param name The name of the patch. + * If null, the patch is named "Patch" and will not be loaded by [PatchLoader]. + * @param description The description of the patch. + * @param use Weather or not the patch should be used. + * @param block The block to build the patch. + * + * @return The created [ResourcePatch]. + */ +fun resourcePatch( + name: String? = null, + nameKey: String? = null, + description: String? = null, + descriptionKey: String? = null, + use: Boolean = true, + block: ResourcePatchBuilder.() -> Unit = {}, +) = ResourcePatchBuilder(name, nameKey,description, descriptionKey, use).buildPatch(block) as ResourcePatch + /** * An exception thrown when patching. * diff --git a/src/main/kotlin/app/morphe/patcher/patch/PatchLocalization.kt b/src/main/kotlin/app/morphe/patcher/patch/PatchLocalization.kt new file mode 100644 index 00000000..57d618ac --- /dev/null +++ b/src/main/kotlin/app/morphe/patcher/patch/PatchLocalization.kt @@ -0,0 +1,48 @@ +package app.morphe.patcher.patch + +import java.util.Collections +import java.util.Locale +import java.util.MissingResourceException +import java.util.ResourceBundle + +object PatchLocalization { + private const val STRING_RESOURCE_FILE = "patch-strings/strings" + + @Volatile + private var currentLocale: Locale = Locale.getDefault() + + private val cache = Collections.synchronizedMap( + HashMap() + ) + + /** + * Sets the locale for localized [Patch] and [Option] name/title/description/etc. + */ + fun setLocale(locale: Locale) { + currentLocale = locale + cache.clear() // Ensure bundles reload with the new locale. + } + + private fun getPatchStrings(clazz: Class<*>): ResourceBundle? { + val loader = clazz.classLoader + + return cache.getOrPut(loader) { + try { + ResourceBundle.getBundle(STRING_RESOURCE_FILE, currentLocale, loader) + } catch (_: MissingResourceException) { + null + } + } + } + + internal fun getLocalizedString(clazz: Class<*>, key: String): String? { + val bundle = getPatchStrings(clazz) ?: return null + + return try { + bundle.getString(key) + } catch (_: MissingResourceException) { + throw IllegalArgumentException("key: $key") // FIXME +// null + } + } +} From 839a37dae87026bd114c0455764634802044c66b Mon Sep 17 00:00:00 2001 From: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Date: Fri, 16 Jan 2026 18:58:32 +0100 Subject: [PATCH 2/2] chore: Add binary backwards compatibility wrappers --- .../kotlin/app/morphe/patcher/patch/Option.kt | 1004 +++++++++++++++-- .../kotlin/app/morphe/patcher/patch/Patch.kt | 66 ++ 2 files changed, 989 insertions(+), 81 deletions(-) diff --git a/src/main/kotlin/app/morphe/patcher/patch/Option.kt b/src/main/kotlin/app/morphe/patcher/patch/Option.kt index 290489c9..a008acf7 100644 --- a/src/main/kotlin/app/morphe/patcher/patch/Option.kt +++ b/src/main/kotlin/app/morphe/patcher/patch/Option.kt @@ -252,7 +252,9 @@ fun stringOption( key: String, default: String? = null, values: Map? = null, + titleKey: String? = null, title: String? = null, + descriptionKey: String? = null, description: String? = null, required: Boolean = false, validator: Option.(String?) -> Boolean = { true }, @@ -260,7 +262,83 @@ fun stringOption( key, default, values, + titleKey, title, + descriptionKey, + description, + required, + validator, +) + +/** + * Create a new [Option] with a string value. + * + * @param key The key. + * @param default The default value. + * @param values Eligible option values mapped to a human-readable name. + * @param title The title. + * @param description A description. + * @param required Whether the option is required. + * @param validator The function to validate the option value. + * + * @return The created [Option]. + * + * @see Option + */ +//@Deprecated("Binary backwards compatibility. Delete after next major version change.") +fun stringOption( + key: String, + default: String? = null, + values: Map? = null, + title: String? = null, + description: String? = null, + required: Boolean = false, + validator: Option.(String?) -> Boolean = { true }, +) = option( + key, + default, + values, + null, + title, + null, + description, + required, + validator, +) + + +/** + * Create a new [Option] with a string value and add it to the current [PatchBuilder]. + * + * @param key The key. + * @param default The default value. + * @param values Eligible option values mapped to a human-readable name. + * @param title The title. + * @param description A description. + * @param required Whether the option is required. + * @param validator The function to validate the option value. + * + * @return The created [Option]. + * + * @see Option + */ +fun PatchBuilder<*>.stringOption( + key: String, + default: String? = null, + values: Map? = null, + titleKey: String? = null, + title: String? = null, + descriptionKey: String? = null, + description: String? = null, + required: Boolean = false, + validator: Option.(String?) -> Boolean = { true }, +) = option( + key, + default, + values, + titleKey, + title, + descriptionKey, description, required, validator, @@ -281,26 +359,649 @@ fun stringOption( * * @see Option */ -fun PatchBuilder<*>.stringOption( +//@Deprecated("Binary backwards compatibility. Delete after next major version change.") +fun PatchBuilder<*>.stringOption( + key: String, + default: String? = null, + values: Map? = null, + title: String? = null, + description: String? = null, + required: Boolean = false, + validator: Option.(String?) -> Boolean = { true }, +) = option( + key, + default, + values, + null, + title, + null, + description, + required, + validator, +) + +/** + * Create a new [Option] with an integer value. + * + * @param key The key. + * @param default The default value. + * @param values Eligible option values mapped to a human-readable name. + * @param title The title. + * @param description A description. + * @param required Whether the option is required. + * @param validator The function to validate the option value. + * + * @return The created [Option]. + * + * @see Option + */ +fun intOption( + key: String, + default: Int? = null, + values: Map? = null, + titleKey: String? = null, + title: String? = null, + descriptionKey: String? = null, + description: String? = null, + required: Boolean = false, + validator: Option.(Int?) -> Boolean = { true }, +) = option( + key, + default, + values, + titleKey, + title, + descriptionKey, + description, + required, + validator, +) + +/** + * Create a new [Option] with an integer value. + * + * @param key The key. + * @param default The default value. + * @param values Eligible option values mapped to a human-readable name. + * @param title The title. + * @param description A description. + * @param required Whether the option is required. + * @param validator The function to validate the option value. + * + * @return The created [Option]. + * + * @see Option + */ +//@Deprecated("Binary backwards compatibility. Delete after next major version change.") +fun intOption( + key: String, + default: Int? = null, + values: Map? = null, + title: String? = null, + description: String? = null, + required: Boolean = false, + validator: Option.(Int?) -> Boolean = { true }, +) = option( + key, + default, + values, + null, + title, + null, + description, + required, + validator, +) + +/** + * Create a new [Option] with an integer value and add it to the current [PatchBuilder]. + * + * @param key The key. + * @param default The default value. + * @param values Eligible option values mapped to a human-readable name. + * @param title The title. + * @param description A description. + * @param required Whether the option is required. + * @param validator The function to validate the option value. + * + * @return The created [Option]. + * + * @see Option + */ +fun PatchBuilder<*>.intOption( + key: String, + default: Int? = null, + values: Map? = null, + titleKey: String? = null, + title: String? = null, + descriptionKey: String? = null, + description: String? = null, + required: Boolean = false, + validator: Option.(Int?) -> Boolean = { true }, +) = option( + key, + default, + values, + titleKey, + title, + descriptionKey, + description, + required, + validator, +) + +/** + * Create a new [Option] with an integer value and add it to the current [PatchBuilder]. + * + * @param key The key. + * @param default The default value. + * @param values Eligible option values mapped to a human-readable name. + * @param title The title. + * @param description A description. + * @param required Whether the option is required. + * @param validator The function to validate the option value. + * + * @return The created [Option]. + * + * @see Option + */ +//@Deprecated("Binary backwards compatibility. Delete after next major version change.") +fun PatchBuilder<*>.intOption( + key: String, + default: Int? = null, + values: Map? = null, + title: String? = null, + description: String? = null, + required: Boolean = false, + validator: Option.(Int?) -> Boolean = { true }, +) = option( + key, + default, + values, + null, + title, + null, + description, + required, + validator, +) + +/** + * Create a new [Option] with a boolean value. + * + * @param key The key. + * @param default The default value. + * @param values Eligible option values mapped to a human-readable name. + * @param title The title. + * @param description A description. + * @param required Whether the option is required. + * @param validator The function to validate the option value. + * + * @return The created [Option]. + * + * @see Option + */ +fun booleanOption( + key: String, + default: Boolean? = null, + values: Map? = null, + titleKey: String? = null, + title: String? = null, + descriptionKey: String? = null, + description: String? = null, + required: Boolean = false, + validator: Option.(Boolean?) -> Boolean = { true }, +) = option( + key, + default, + values, + titleKey, + title, + descriptionKey, + description, + required, + validator, +) + +/** + * Create a new [Option] with a boolean value. + * + * @param key The key. + * @param default The default value. + * @param values Eligible option values mapped to a human-readable name. + * @param title The title. + * @param description A description. + * @param required Whether the option is required. + * @param validator The function to validate the option value. + * + * @return The created [Option]. + * + * @see Option + */ +//@Deprecated("Binary backwards compatibility. Delete after next major version change.") +fun booleanOption( + key: String, + default: Boolean? = null, + values: Map? = null, + title: String? = null, + description: String? = null, + required: Boolean = false, + validator: Option.(Boolean?) -> Boolean = { true }, +) = option( + key, + default, + values, + null, + title, + null, + description, + required, + validator, +) + + +/** + * Create a new [Option] with a boolean value and add it to the current [PatchBuilder]. + * + * @param key The key. + * @param default The default value. + * @param values Eligible option values mapped to a human-readable name. + * @param title The title. + * @param description A description. + * @param required Whether the option is required. + * @param validator The function to validate the option value. + * + * @return The created [Option]. + * + * @see Option + */ +fun PatchBuilder<*>.booleanOption( + key: String, + default: Boolean? = null, + values: Map? = null, + titleKey: String? = null, + title: String? = null, + descriptionKey: String? = null, + description: String? = null, + required: Boolean = false, + validator: Option.(Boolean?) -> Boolean = { true }, +) = option( + key, + default, + values, + titleKey, + title, + descriptionKey, + description, + required, + validator, +) + +/** + * Create a new [Option] with a boolean value and add it to the current [PatchBuilder]. + * + * @param key The key. + * @param default The default value. + * @param values Eligible option values mapped to a human-readable name. + * @param title The title. + * @param description A description. + * @param required Whether the option is required. + * @param validator The function to validate the option value. + * + * @return The created [Option]. + * + * @see Option + */ +//@Deprecated("Binary backwards compatibility. Delete after next major version change.") +fun PatchBuilder<*>.booleanOption( + key: String, + default: Boolean? = null, + values: Map? = null, + title: String? = null, + description: String? = null, + required: Boolean = false, + validator: Option.(Boolean?) -> Boolean = { true }, +) = option( + key, + default, + values, + null, + title, + null, + description, + required, + validator, +) + +/** + * Create a new [Option] with a float value. + * + * @param key The key. + * @param default The default value. + * @param values Eligible option values mapped to a human-readable name. + * @param title The title. + * @param description A description. + * @param required Whether the option is required. + * @param validator The function to validate the option value. + * + * @return The created [Option]. + * + * @see Option + */ +fun floatOption( + key: String, + default: Float? = null, + values: Map? = null, + titleKey: String? = null, + title: String? = null, + descriptionKey: String? = null, + description: String? = null, + required: Boolean = false, + validator: Option.(Float?) -> Boolean = { true }, +) = option( + key, + default, + values, + titleKey, + title, + descriptionKey, + description, + required, + validator, +) + +/** + * Create a new [Option] with a float value. + * + * @param key The key. + * @param default The default value. + * @param values Eligible option values mapped to a human-readable name. + * @param title The title. + * @param description A description. + * @param required Whether the option is required. + * @param validator The function to validate the option value. + * + * @return The created [Option]. + * + * @see Option + */ +//@Deprecated("Binary backwards compatibility. Delete after next major version change.") +fun floatOption( + key: String, + default: Float? = null, + values: Map? = null, + title: String? = null, + description: String? = null, + required: Boolean = false, + validator: Option.(Float?) -> Boolean = { true }, +) = option( + key, + default, + values, + null, + title, + null, + description, + required, + validator, +) + + +/** + * Create a new [Option] with a float value and add it to the current [PatchBuilder]. + * + * @param key The key. + * @param default The default value. + * @param values Eligible option values mapped to a human-readable name. + * @param title The title. + * @param description A description. + * @param required Whether the option is required. + * @param validator The function to validate the option value. + * + * @return The created [Option]. + * + * @see Option + */ +fun PatchBuilder<*>.floatOption( + key: String, + default: Float? = null, + values: Map? = null, + titleKey: String? = null, + title: String? = null, + descriptionKey: String? = null, + description: String? = null, + required: Boolean = false, + validator: Option.(Float?) -> Boolean = { true }, +) = option( + key, + default, + values, + titleKey, + title, + descriptionKey, + description, + required, + validator, +) + +/** + * Create a new [Option] with a float value and add it to the current [PatchBuilder]. + * + * @param key The key. + * @param default The default value. + * @param values Eligible option values mapped to a human-readable name. + * @param title The title. + * @param description A description. + * @param required Whether the option is required. + * @param validator The function to validate the option value. + * + * @return The created [Option]. + * + * @see Option + */ +//@Deprecated("Binary backwards compatibility. Delete after next major version change.") +fun PatchBuilder<*>.floatOption( + key: String, + default: Float? = null, + values: Map? = null, + title: String? = null, + description: String? = null, + required: Boolean = false, + validator: Option.(Float?) -> Boolean = { true }, +) = option( + key, + default, + values, + null, + title, + null, + description, + required, + validator, +) + + +/** + * Create a new [Option] with a long value. + * + * @param key The key. + * @param default The default value. + * @param values Eligible option values mapped to a human-readable name. + * @param title The title. + * @param description A description. + * @param required Whether the option is required. + * @param validator The function to validate the option value. + * + * @return The created [Option]. + * + * @see Option + */ +fun longOption( + key: String, + default: Long? = null, + values: Map? = null, + titleKey: String? = null, + title: String? = null, + descriptionKey: String? = null, + description: String? = null, + required: Boolean = false, + validator: Option.(Long?) -> Boolean = { true }, +) = option( + key, + default, + values, + titleKey, + title, + descriptionKey, + description, + required, + validator, +) + +/** + * @param key The key. + * @param default The default value. + * @param values Eligible option values mapped to a human-readable name. + * @param title The title. + * @param description A description. + * @param required Whether the option is required. + * @param validator The function to validate the option value. + * + * @see Option + */ +//@Deprecated("Binary backwards compatibility. Delete after next major version change.") +fun longOption( + key: String, + default: Long? = null, + values: Map? = null, + title: String? = null, + description: String? = null, + required: Boolean = false, + validator: Option.(Long?) -> Boolean = { true }, +) = option( + key, + default, + values, + null, + title, + null, + description, + required, + validator, +) + +/** + * Create a new [Option] with a long value and add it to the current [PatchBuilder]. + * + * @param key The key. + * @param default The default value. + * @param values Eligible option values mapped to a human-readable name. + * @param title The title. + * @param description A description. + * @param required Whether the option is required. + * @param validator The function to validate the option value. + * + * @return The created [Option]. + * + * @see Option + */ +fun PatchBuilder<*>.longOption( + key: String, + default: Long? = null, + values: Map? = null, + titleKey: String? = null, + title: String? = null, + descriptionKey: String? = null, + description: String? = null, + required: Boolean = false, + validator: Option.(Long?) -> Boolean = { true }, +) = option( + key, + default, + values, + titleKey, + title, + descriptionKey, + description, + required, + validator, +) + +/** + * Create a new [Option] with a long value and add it to the current [PatchBuilder]. + * + * @param key The key. + * @param default The default value. + * @param values Eligible option values mapped to a human-readable name. + * @param title The title. + * @param description A description. + * @param required Whether the option is required. + * @param validator The function to validate the option value. + * + * @return The created [Option]. + * + * @see Option + */ +//@Deprecated("Binary backwards compatibility. Delete after next major version change.") +fun PatchBuilder<*>.longOption( + key: String, + default: Long? = null, + values: Map? = null, + title: String? = null, + description: String? = null, + required: Boolean = false, + validator: Option.(Long?) -> Boolean = { true }, +) = option( + key, + default, + values, + null, + title, + null, + description, + required, + validator, +) + +/** + * Create a new [Option] with a string list value. + * + * @param key The key. + * @param default The default value. + * @param values Eligible option values mapped to a human-readable name. + * @param title The title. + * @param description A description. + * @param required Whether the option is required. + * @param validator The function to validate the option value. + * + * @return The created [Option]. + * + * @see Option + */ +fun stringsOption( key: String, - default: String? = null, - values: Map? = null, + default: List? = null, + values: Map?>? = null, + titleKey: String? = null, title: String? = null, + descriptionKey: String? = null, description: String? = null, required: Boolean = false, - validator: Option.(String?) -> Boolean = { true }, + validator: Option>.(List?) -> Boolean = { true }, ) = option( key, default, values, + titleKey, title, + descriptionKey, description, required, validator, ) /** - * Create a new [Option] with an integer value. + * Create a new [Option] with a string list value. * * @param key The key. * @param default The default value. @@ -314,26 +1015,29 @@ fun PatchBuilder<*>.stringOption( * * @see Option */ -fun intOption( +//@Deprecated("Binary backwards compatibility. Delete after next major version change.") +fun stringsOption( key: String, - default: Int? = null, - values: Map? = null, + default: List? = null, + values: Map?>? = null, title: String? = null, description: String? = null, required: Boolean = false, - validator: Option.(Int?) -> Boolean = { true }, + validator: Option>.(List?) -> Boolean = { true }, ) = option( key, default, values, + null, title, + null, description, required, validator, ) /** - * Create a new [Option] with an integer value and add it to the current [PatchBuilder]. + * Create a new [Option] with a string list value and add it to the current [PatchBuilder]. * * @param key The key. * @param default The default value. @@ -347,26 +1051,30 @@ fun intOption( * * @see Option */ -fun PatchBuilder<*>.intOption( +fun PatchBuilder<*>.stringsOption( key: String, - default: Int? = null, - values: Map? = null, + default: List? = null, + values: Map?>? = null, + titleKey: String? = null, title: String? = null, + descriptionKey: String? = null, description: String? = null, required: Boolean = false, - validator: Option.(Int?) -> Boolean = { true }, + validator: Option>.(List?) -> Boolean = { true }, ) = option( key, default, values, + titleKey, title, + descriptionKey, description, required, validator, ) /** - * Create a new [Option] with a boolean value. + * Create a new [Option] with a string list value and add it to the current [PatchBuilder]. * * @param key The key. * @param default The default value. @@ -380,26 +1088,30 @@ fun PatchBuilder<*>.intOption( * * @see Option */ -fun booleanOption( +//@Deprecated("Binary backwards compatibility. Delete after next major version change.") +fun PatchBuilder<*>.stringsOption( key: String, - default: Boolean? = null, - values: Map? = null, + default: List? = null, + values: Map?>? = null, title: String? = null, description: String? = null, required: Boolean = false, - validator: Option.(Boolean?) -> Boolean = { true }, + validator: Option>.(List?) -> Boolean = { true }, ) = option( key, default, values, + null, title, + null, description, required, validator, ) + /** - * Create a new [Option] with a boolean value and add it to the current [PatchBuilder]. + * Create a new [Option] with an integer list value. * * @param key The key. * @param default The default value. @@ -413,26 +1125,30 @@ fun booleanOption( * * @see Option */ -fun PatchBuilder<*>.booleanOption( +fun intsOption( key: String, - default: Boolean? = null, - values: Map? = null, + default: List? = null, + values: Map?>? = null, + titleKey: String? = null, title: String? = null, + descriptionKey: String? = null, description: String? = null, required: Boolean = false, - validator: Option.(Boolean?) -> Boolean = { true }, + validator: Option>.(List?) -> Boolean = { true }, ) = option( key, default, values, + titleKey, title, + descriptionKey, description, required, validator, ) /** - * Create a new [Option] with a float value. + * Create a new [Option] with an integer list value. * * @param key The key. * @param default The default value. @@ -446,26 +1162,29 @@ fun PatchBuilder<*>.booleanOption( * * @see Option */ -fun floatOption( +//@Deprecated("Binary backwards compatibility. Delete after next major version change.") +fun intsOption( key: String, - default: Float? = null, - values: Map? = null, + default: List? = null, + values: Map?>? = null, title: String? = null, description: String? = null, required: Boolean = false, - validator: Option.(Float?) -> Boolean = { true }, + validator: Option>.(List?) -> Boolean = { true }, ) = option( key, default, values, + null, title, + null, description, required, validator, ) /** - * Create a new [Option] with a float value and add it to the current [PatchBuilder]. + * Create a new [Option] with an integer list value and add it to the current [PatchBuilder]. * * @param key The key. * @param default The default value. @@ -479,26 +1198,30 @@ fun floatOption( * * @see Option */ -fun PatchBuilder<*>.floatOption( +fun PatchBuilder<*>.intsOption( key: String, - default: Float? = null, - values: Map? = null, + default: List? = null, + values: Map?>? = null, + titleKey: String? = null, title: String? = null, + descriptionKey: String? = null, description: String? = null, required: Boolean = false, - validator: Option.(Float?) -> Boolean = { true }, + validator: Option>.(List?) -> Boolean = { true }, ) = option( key, default, values, + titleKey, title, + descriptionKey, description, required, validator, ) /** - * Create a new [Option] with a long value. + * Create a new [Option] with an integer list value and add it to the current [PatchBuilder]. * * @param key The key. * @param default The default value. @@ -512,26 +1235,29 @@ fun PatchBuilder<*>.floatOption( * * @see Option */ -fun longOption( +//@Deprecated("Binary backwards compatibility. Delete after next major version change.") +fun PatchBuilder<*>.intsOption( key: String, - default: Long? = null, - values: Map? = null, + default: List? = null, + values: Map?>? = null, title: String? = null, description: String? = null, required: Boolean = false, - validator: Option.(Long?) -> Boolean = { true }, + validator: Option>.(List?) -> Boolean = { true }, ) = option( key, default, values, + null, title, + null, description, required, validator, ) /** - * Create a new [Option] with a long value and add it to the current [PatchBuilder]. + * Create a new [Option] with a boolean list value. * * @param key The key. * @param default The default value. @@ -545,26 +1271,30 @@ fun longOption( * * @see Option */ -fun PatchBuilder<*>.longOption( +fun booleansOption( key: String, - default: Long? = null, - values: Map? = null, + default: List? = null, + values: Map?>? = null, + titleKey: String? = null, title: String? = null, + descriptionKey: String? = null, description: String? = null, required: Boolean = false, - validator: Option.(Long?) -> Boolean = { true }, + validator: Option>.(List?) -> Boolean = { true }, ) = option( key, default, values, + titleKey, title, + descriptionKey, description, required, validator, ) /** - * Create a new [Option] with a string list value. + * Create a new [Option] with a boolean list value. * * @param key The key. * @param default The default value. @@ -578,26 +1308,29 @@ fun PatchBuilder<*>.longOption( * * @see Option */ -fun stringsOption( +//@Deprecated("Binary backwards compatibility. Delete after next major version change.") +fun booleansOption( key: String, - default: List? = null, - values: Map?>? = null, + default: List? = null, + values: Map?>? = null, title: String? = null, description: String? = null, required: Boolean = false, - validator: Option>.(List?) -> Boolean = { true }, + validator: Option>.(List?) -> Boolean = { true }, ) = option( key, default, values, + null, title, + null, description, required, validator, ) /** - * Create a new [Option] with a string list value and add it to the current [PatchBuilder]. + * Create a new [Option] with a boolean list value and add it to the current [PatchBuilder]. * * @param key The key. * @param default The default value. @@ -611,26 +1344,30 @@ fun stringsOption( * * @see Option */ -fun PatchBuilder<*>.stringsOption( +fun PatchBuilder<*>.booleansOption( key: String, - default: List? = null, - values: Map?>? = null, + default: List? = null, + values: Map?>? = null, + titleKey: String? = null, title: String? = null, + descriptionKey: String? = null, description: String? = null, required: Boolean = false, - validator: Option>.(List?) -> Boolean = { true }, + validator: Option>.(List?) -> Boolean = { true }, ) = option( key, default, values, + titleKey, title, + descriptionKey, description, required, validator, ) /** - * Create a new [Option] with an integer list value. + * Create a new [Option] with a boolean list value and add it to the current [PatchBuilder]. * * @param key The key. * @param default The default value. @@ -644,26 +1381,29 @@ fun PatchBuilder<*>.stringsOption( * * @see Option */ -fun intsOption( +//@Deprecated("Binary backwards compatibility. Delete after next major version change.") +fun PatchBuilder<*>.booleansOption( key: String, - default: List? = null, - values: Map?>? = null, + default: List? = null, + values: Map?>? = null, title: String? = null, description: String? = null, required: Boolean = false, - validator: Option>.(List?) -> Boolean = { true }, + validator: Option>.(List?) -> Boolean = { true }, ) = option( key, default, values, + null, title, + null, description, required, validator, ) /** - * Create a new [Option] with an integer list value and add it to the current [PatchBuilder]. + * Create a new [Option] with a float list value and add it to the current [PatchBuilder]. * * @param key The key. * @param default The default value. @@ -677,26 +1417,30 @@ fun intsOption( * * @see Option */ -fun PatchBuilder<*>.intsOption( +fun PatchBuilder<*>.floatsOption( key: String, - default: List? = null, - values: Map?>? = null, + default: List? = null, + values: Map?>? = null, + titleKey: String? = null, title: String? = null, + descriptionKey: String? = null, description: String? = null, required: Boolean = false, - validator: Option>.(List?) -> Boolean = { true }, + validator: Option>.(List?) -> Boolean = { true }, ) = option( key, default, values, + titleKey, title, + descriptionKey, description, required, validator, ) /** - * Create a new [Option] with a boolean list value. + * Create a new [Option] with a float list value and add it to the current [PatchBuilder]. * * @param key The key. * @param default The default value. @@ -710,26 +1454,29 @@ fun PatchBuilder<*>.intsOption( * * @see Option */ -fun booleansOption( +//@Deprecated("Binary backwards compatibility. Delete after next major version change.") +fun PatchBuilder<*>.floatsOption( key: String, - default: List? = null, - values: Map?>? = null, + default: List? = null, + values: Map?>? = null, title: String? = null, description: String? = null, required: Boolean = false, - validator: Option>.(List?) -> Boolean = { true }, + validator: Option>.(List?) -> Boolean = { true }, ) = option( key, default, values, + null, title, + null, description, required, validator, ) /** - * Create a new [Option] with a boolean list value and add it to the current [PatchBuilder]. + * Create a new [Option] with a long list value. * * @param key The key. * @param default The default value. @@ -743,26 +1490,30 @@ fun booleansOption( * * @see Option */ -fun PatchBuilder<*>.booleansOption( +fun longsOption( key: String, - default: List? = null, - values: Map?>? = null, + default: List? = null, + values: Map?>? = null, + titleKey: String? = null, title: String? = null, + descriptionKey: String? = null, description: String? = null, required: Boolean = false, - validator: Option>.(List?) -> Boolean = { true }, + validator: Option>.(List?) -> Boolean = { true }, ) = option( key, default, values, + titleKey, title, + descriptionKey, description, required, validator, ) /** - * Create a new [Option] with a float list value and add it to the current [PatchBuilder]. + * Create a new [Option] with a long list value. * * @param key The key. * @param default The default value. @@ -776,26 +1527,29 @@ fun PatchBuilder<*>.booleansOption( * * @see Option */ -fun PatchBuilder<*>.floatsOption( +//@Deprecated("Binary backwards compatibility. Delete after next major version change.") +fun longsOption( key: String, - default: List? = null, - values: Map?>? = null, + default: List? = null, + values: Map?>? = null, title: String? = null, description: String? = null, required: Boolean = false, - validator: Option>.(List?) -> Boolean = { true }, + validator: Option>.(List?) -> Boolean = { true }, ) = option( key, default, values, + null, title, + null, description, required, validator, ) /** - * Create a new [Option] with a long list value. + * Create a new [Option] with a long list value and add it to the current [PatchBuilder]. * * @param key The key. * @param default The default value. @@ -809,11 +1563,13 @@ fun PatchBuilder<*>.floatsOption( * * @see Option */ -fun longsOption( +fun PatchBuilder<*>.longsOption( key: String, default: List? = null, values: Map?>? = null, + titleKey: String? = null, title: String? = null, + descriptionKey: String? = null, description: String? = null, required: Boolean = false, validator: Option>.(List?) -> Boolean = { true }, @@ -821,7 +1577,9 @@ fun longsOption( key, default, values, + titleKey, title, + descriptionKey, description, required, validator, @@ -842,6 +1600,7 @@ fun longsOption( * * @see Option */ +//@Deprecated("Binary backwards compatibility. Delete after next major version change.") fun PatchBuilder<*>.longsOption( key: String, default: List? = null, @@ -854,9 +1613,49 @@ fun PatchBuilder<*>.longsOption( key, default, values, + null, + title, + null, + description, + required, + validator, +) + +/** + * Create a new [Option]. + * + * @param key The key. + * @param default The default value. + * @param values Eligible option values mapped to a human-readable name. + * @param title The title. + * @param description A description. + * @param required Whether the option is required. + * @param validator The function to validate the option value. + * + * @return The created [Option]. + * + * @see Option + */ +inline fun option( + key: String, + default: T? = null, + values: Map? = null, + titleKey: String? = null, + title: String? = null, + descriptionKey: String? = null, + description: String? = null, + required: Boolean = false, + noinline validator: Option.(T?) -> Boolean = { true }, +) = Option( + key, + default, + values, + titleKey, title, + descriptionKey, description, required, + typeOf(), validator, ) @@ -875,6 +1674,7 @@ fun PatchBuilder<*>.longsOption( * * @see Option */ +//@Deprecated("Binary backwards compatibility. Delete after next major version change.") inline fun option( key: String, default: T? = null, @@ -887,7 +1687,9 @@ inline fun option( key, default, values, + null, title, + null, description, required, typeOf(), @@ -909,6 +1711,44 @@ inline fun option( * * @see Option */ +inline fun PatchBuilder<*>.option( + key: String, + default: T? = null, + values: Map? = null, + titleKey: String? = null, + title: String? = null, + descriptionKey: String? = null, + description: String? = null, + required: Boolean = false, + noinline validator: Option.(T?) -> Boolean = { true }, +) = app.morphe.patcher.patch.option( + key, + default, + values, + titleKey, + title, + descriptionKey, + description, + required, + validator, +).also { it() } + +/** + * Create a new [Option] and add it to the current [PatchBuilder]. + * + * @param key The key. + * @param default The default value. + * @param values Eligible option values mapped to a human-readable name. + * @param title The title. + * @param description A description. + * @param required Whether the option is required. + * @param validator The function to validate the option value. + * + * @return The created [Option]. + * + * @see Option + */ +//@Deprecated("Binary backwards compatibility. Delete after next major version change.") inline fun PatchBuilder<*>.option( key: String, default: T? = null, @@ -921,7 +1761,9 @@ inline fun PatchBuilder<*>.option( key, default, values, + null, title, + null, description, required, validator, diff --git a/src/main/kotlin/app/morphe/patcher/patch/Patch.kt b/src/main/kotlin/app/morphe/patcher/patch/Patch.kt index 2f3746f1..f7d237b0 100644 --- a/src/main/kotlin/app/morphe/patcher/patch/Patch.kt +++ b/src/main/kotlin/app/morphe/patcher/patch/Patch.kt @@ -13,8 +13,11 @@ import java.lang.reflect.Member import java.lang.reflect.Method import java.lang.reflect.Modifier import java.net.URLClassLoader +import java.util.Locale import java.util.function.Supplier import java.util.jar.JarFile +import java.util.logging.Logger +import kotlin.text.lowercase typealias PackageName = String typealias VersionName = String @@ -49,6 +52,43 @@ sealed class Patch>( // if a patch has a finalizing block in order to not emit it twice. internal var finalizeBlock: ((C) -> Unit)?, ) { + + /** + * @param C The [PatchContext] to execute and finalize the patch with. + * @param name The name of the patch. If null then this patch not be loaded by [PatchLoader]. + * @param description The description of the patch. + * @param use Weather or not the patch should be used. + * @param dependencies Other patches this patch depends on. + * @param compatiblePackages The packages the patch is compatible with. + * If null, the patch is compatible with all packages. + * @param options The options of the patch. + * @param executeBlock The execution block of the patch. + * @param finalizeBlock The finalizing block of the patch. Called after all patches have been executed, + * in reverse order of execution. + */ +// @Deprecated("Here only for binary backwards compatibility") + constructor( + name: String?, + description: String?, + use: Boolean, + dependencies: Set>, + compatiblePackages: Set?, + options: Set>, + executeBlock: (C) -> Unit, + finalizeBlock: ((C) -> Unit)?, + ) : this( + name, + null, + description, + null, + use, + dependencies, + compatiblePackages, + options, + executeBlock, + finalizeBlock + ) + /** * The options of the patch. */ @@ -512,6 +552,32 @@ class BytecodePatchBuilder internal constructor( ) } +/** + * Create a new [BytecodePatch]. + * + * @param name The name of the patch. + * If null, the patch is named "Patch" and will not be loaded by [PatchLoader]. + * @param description The description of the patch. + * @param use Weather or not the patch should be used. + * @param block The block to build the patch. + * + * @return The created [BytecodePatch]. + */ +//@Deprecated("Here only for binary backwards compatibility") +fun bytecodePatch( + name: String? = null, + description: String? = null, + use: Boolean = true, + block: BytecodePatchBuilder.() -> Unit = {}, +) = BytecodePatchBuilder( + name = name, + nameKey = null, + description = description, + descriptionKey = null, + use = use, +).buildPatch(block) as BytecodePatch + + /** * Create a new [BytecodePatch]. *