From 39c70d8a2c8c9930f0bf849d27e0736a495c74dd Mon Sep 17 00:00:00 2001 From: EpicPlayerA10 Date: Tue, 15 Oct 2024 17:32:56 +0200 Subject: [PATCH] Use context's classes in InheritanceGraph. Add tests to HP888PackerTransformer --- .../api/classpath/ClassProvider.java | 38 ++ .../api/classpath/ClassStorage.java | 81 ++++ .../deobfuscator/api/classpath/Classpath.java | 118 ----- .../api/classpath/CombinedClassProvider.java | 52 +++ .../api/classpath/JvmClassProvider.java | 82 ++++ .../api/classpath/JvmClasspath.java | 47 -- .../deobfuscator/api/context/Context.java | 114 ++--- .../api/execution/ClasspathDataSupplier.java | 10 +- .../deobfuscator/api/execution/SandBox.java | 6 +- .../deobfuscator/api/helper/AsmHelper.java | 8 +- .../api/inheritance/InheritanceGraph.java | 27 +- .../uwu/narumi/deobfuscator/Deobfuscator.java | 58 +-- .../deobfuscator/TestDeobfuscation.java | 4 +- .../composed/ComposedHP888Transformer.java | 36 +- .../exploit/WebExploitRemoveTransformer.java | 4 +- .../impl/hp888/HP888PackerTransformer.java | 16 +- .../impl/hp888/HP888StringTransformer.java | 15 +- .../ZelixLongEncryptionMPCTransformer.java | 10 +- ...llIIlIllIIlIIIIIllIlIIlIlIllIIIlIIII.class | Bin 0 -> 1982 bytes .../com/bric/colorpicker/ColorPicker$1.mc | Bin 0 -> 736 bytes .../hp888/com/bric/colorpicker/ColorPicker.mc | Bin 0 -> 12496 bytes .../com/bric/colorpicker/ColorPickerMode.mc | Bin 0 -> 1120 bytes ...IIIIlIllIIlIIlllIIllIIIllIIlIlIIllIl.class | Bin 1259 -> 0 bytes .../hp888/pack/MyClassLoader.class | Bin 2053 -> 0 bytes .../hp888/pack/MyFunction2.class | Bin 0 -> 4792 bytes .../com/bric/colorpicker/ColorPicker$1.dec | 36 ++ .../com/bric/colorpicker/ColorPicker.dec | 418 ++++++++++++++++++ .../com/bric/colorpicker/ColorPickerMode.dec | 21 + ...IlIIIIlIllIIlIIlllIIllIIIllIIlIlIIllIl.dec | Bin 69 -> 0 bytes .../hp888/pack/MyClassLoader.dec | 32 -- .../custom-classes/hp888/pack/MyFunction2.dec | Bin 0 -> 4010 bytes 31 files changed, 880 insertions(+), 353 deletions(-) create mode 100644 deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/classpath/ClassProvider.java create mode 100644 deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/classpath/ClassStorage.java delete mode 100644 deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/classpath/Classpath.java create mode 100644 deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/classpath/CombinedClassProvider.java create mode 100644 deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/classpath/JvmClassProvider.java delete mode 100644 deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/classpath/JvmClasspath.java create mode 100644 testData/compiled/custom-classes/hp888/IIIlIIIlllIIIlllIIlIllIIlIIIIIllIlIIlIlIllIIIlIIII.class create mode 100644 testData/compiled/custom-classes/hp888/com/bric/colorpicker/ColorPicker$1.mc create mode 100644 testData/compiled/custom-classes/hp888/com/bric/colorpicker/ColorPicker.mc create mode 100644 testData/compiled/custom-classes/hp888/com/bric/colorpicker/ColorPickerMode.mc delete mode 100644 testData/compiled/custom-classes/hp888/lIllIIllllllIlIIIIlIllIIlIIlllIIllIIIllIIlIlIIllIl.class delete mode 100644 testData/compiled/custom-classes/hp888/pack/MyClassLoader.class create mode 100644 testData/compiled/custom-classes/hp888/pack/MyFunction2.class create mode 100644 testData/results/custom-classes/hp888/com/bric/colorpicker/ColorPicker$1.dec create mode 100644 testData/results/custom-classes/hp888/com/bric/colorpicker/ColorPicker.dec create mode 100644 testData/results/custom-classes/hp888/com/bric/colorpicker/ColorPickerMode.dec delete mode 100644 testData/results/custom-classes/hp888/lIllIIllllllIlIIIIlIllIIlIIlllIIllIIIllIIlIlIIllIl.dec delete mode 100644 testData/results/custom-classes/hp888/pack/MyClassLoader.dec create mode 100644 testData/results/custom-classes/hp888/pack/MyFunction2.dec diff --git a/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/classpath/ClassProvider.java b/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/classpath/ClassProvider.java new file mode 100644 index 0000000..5d93fb6 --- /dev/null +++ b/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/classpath/ClassProvider.java @@ -0,0 +1,38 @@ +package uwu.narumi.deobfuscator.api.classpath; + +import org.jetbrains.annotations.Nullable; +import org.objectweb.asm.tree.ClassNode; + +import java.util.Collection; + +public interface ClassProvider { + /** + * Gets class bytes by internal name + * + * @param name Internal name of class + * @return Class bytes + */ + byte @Nullable [] getClass(String name); + + /** + * Gets file bytes by name + * + * @param path File path + * @return File bytes + */ + byte @Nullable [] getFile(String path); + + /** + * Gets class node that holds only the class information. It is not guaranteed that the class holds code. + * + * @param name Internal name of class + * @return Class node + */ + @Nullable + ClassNode getClassInfo(String name); + + /** + * Gets all classes in the provider. + */ + Collection getLoadedClasses(); +} diff --git a/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/classpath/ClassStorage.java b/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/classpath/ClassStorage.java new file mode 100644 index 0000000..30c350b --- /dev/null +++ b/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/classpath/ClassStorage.java @@ -0,0 +1,81 @@ +package uwu.narumi.deobfuscator.api.classpath; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.objectweb.asm.tree.ClassNode; +import software.coley.cafedude.InvalidClassException; +import uwu.narumi.deobfuscator.api.helper.ClassHelper; +import uwu.narumi.deobfuscator.api.helper.FileHelper; + +import java.nio.file.Path; +import java.util.Collection; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class ClassStorage implements ClassProvider { + private final Map compiledClasses = new ConcurrentHashMap<>(); + private final Map files = new ConcurrentHashMap<>(); + + private final Map classesInfo = new ConcurrentHashMap<>(); + + /** + * Adds jar to class storage + * + * @param jarPath Jar path + */ + public void addJar(@NotNull Path jarPath) { + FileHelper.loadFilesFromZip(jarPath, (classPath, bytes) -> { + if (!ClassHelper.isClass(classPath, bytes)) { + files.putIfAbsent(classPath, bytes); + return; + } + + addRawClass(bytes); + }); + } + + public void addRawClass(byte[] bytes) { + try { + ClassNode classNode = ClassHelper.loadUnknownClassInfo(bytes); + String className = classNode.name; + + // Add class to class storage + compiledClasses.putIfAbsent(className, bytes); + classesInfo.putIfAbsent(className, classNode); + } catch (InvalidClassException e) { + throw new RuntimeException(e); + } + } + + @Override + public byte @Nullable [] getClass(String name) { + return compiledClasses.get(name); + } + + @Override + public byte @Nullable [] getFile(String path) { + return files.get(path); + } + + @Override + public @Nullable ClassNode getClassInfo(String name) { + return classesInfo.get(name); + } + + @Override + public Collection getLoadedClasses() { + return compiledClasses.keySet(); + } + + public Map compiledClasses() { + return compiledClasses; + } + + public Map files() { + return files; + } + + public Map classesInfo() { + return classesInfo; + } +} diff --git a/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/classpath/Classpath.java b/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/classpath/Classpath.java deleted file mode 100644 index dcf739d..0000000 --- a/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/classpath/Classpath.java +++ /dev/null @@ -1,118 +0,0 @@ -package uwu.narumi.deobfuscator.api.classpath; - -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; -import org.objectweb.asm.tree.ClassNode; -import uwu.narumi.deobfuscator.api.context.DeobfuscatorOptions; -import uwu.narumi.deobfuscator.api.helper.ClassHelper; -import uwu.narumi.deobfuscator.api.helper.FileHelper; - -/** - * Immutable classpath - * - * @param classesInfo Class nodes that hold only the class information, not code - */ -public record Classpath( - Map rawClasses, - Map files, - Map classesInfo -) { - - public static Builder builder() { - return new Builder(); - } - - public static class Builder { - private static final Logger LOGGER = LogManager.getLogger(); - - private final Map rawClasses = new HashMap<>(); - private final Map files = new HashMap<>(); - - private final Map classesInfo = new HashMap<>(); - - private Builder() { - } - - /** - * Adds jar to classpath - * - * @param jarPath Jar path - */ - @Contract("_ -> this") - public Builder addJar(@NotNull Path jarPath) { - FileHelper.loadFilesFromZip(jarPath, (classPath, bytes) -> { - if (!ClassHelper.isClass(classPath, bytes)) { - files.putIfAbsent(classPath, bytes); - return; - } - - try { - ClassNode classNode = ClassHelper.loadUnknownClassInfo(bytes); - String className = classNode.name; - - rawClasses.putIfAbsent(className, bytes); - classesInfo.putIfAbsent(className, classNode); - } catch (Exception e) { - LOGGER.error("Could not load {} class from {} library", classPath, jarPath, e); - } - }); - - return this; - } - - /** - * Adds {@link DeobfuscatorOptions.ExternalFile} to classpath - * - * @param externalFile External class - */ - @Contract("_ -> this") - public Builder addExternalFile(DeobfuscatorOptions.ExternalFile externalFile) { - try { - byte[] bytes = Files.readAllBytes(externalFile.path()); - if (!ClassHelper.isClass(externalFile.pathInJar(), bytes)) { - files.putIfAbsent(externalFile.pathInJar(), bytes); - return this; - } - - ClassNode classNode = ClassHelper.loadUnknownClassInfo(bytes); - String className = classNode.name; - - // Add class to classpath - rawClasses.putIfAbsent(className, bytes); - classesInfo.putIfAbsent(className, classNode); - } catch (Exception e) { - throw new RuntimeException(e); - } - - return this; - } - - /** - * Adds another classpath to this classpath - */ - @Contract("_ -> this") - public Builder addClasspath(Classpath classpath) { - this.rawClasses.putAll(classpath.rawClasses); - this.files.putAll(classpath.files); - this.classesInfo.putAll(classpath.classesInfo); - - return this; - } - - public Classpath build() { - return new Classpath( - Collections.unmodifiableMap(rawClasses), - Collections.unmodifiableMap(files), - Collections.unmodifiableMap(classesInfo) - ); - } - } -} diff --git a/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/classpath/CombinedClassProvider.java b/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/classpath/CombinedClassProvider.java new file mode 100644 index 0000000..ad3f20c --- /dev/null +++ b/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/classpath/CombinedClassProvider.java @@ -0,0 +1,52 @@ +package uwu.narumi.deobfuscator.api.classpath; + +import org.jetbrains.annotations.Nullable; +import org.objectweb.asm.tree.ClassNode; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +public class CombinedClassProvider implements ClassProvider { + private final ClassProvider[] classProviders; + + public CombinedClassProvider(ClassProvider... classProviders) { + this.classProviders = classProviders; + } + + @Override + public byte @Nullable [] getClass(String name) { + for (ClassProvider classProvider : this.classProviders) { + byte[] bytes = classProvider.getClass(name); + if (bytes != null) return bytes; + } + return null; + } + + @Override + public byte @Nullable [] getFile(String path) { + for (ClassProvider classProvider : this.classProviders) { + byte[] bytes = classProvider.getFile(path); + if (bytes != null) return bytes; + } + return null; + } + + @Override + public @Nullable ClassNode getClassInfo(String name) { + for (ClassProvider classProvider : this.classProviders) { + ClassNode classInfo = classProvider.getClassInfo(name); + if (classInfo != null) return classInfo; + } + return null; + } + + @Override + public Collection getLoadedClasses() { + Set classes = new HashSet<>(); + for (ClassProvider classProvider : this.classProviders) { + classes.addAll(classProvider.getLoadedClasses()); + } + return classes; + } +} diff --git a/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/classpath/JvmClassProvider.java b/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/classpath/JvmClassProvider.java new file mode 100644 index 0000000..fd1cc99 --- /dev/null +++ b/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/classpath/JvmClassProvider.java @@ -0,0 +1,82 @@ +package uwu.narumi.deobfuscator.api.classpath; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.jetbrains.annotations.Nullable; +import org.objectweb.asm.tree.ClassNode; +import uwu.narumi.deobfuscator.api.helper.ClassHelper; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Classpath that fetches default JVM classes + */ +public class JvmClassProvider implements ClassProvider { + private static final Logger LOGGER = LogManager.getLogger(); + + public static final JvmClassProvider INSTANCE = new JvmClassProvider(); + + private final Map classesCache = new ConcurrentHashMap<>(); + private final Map classInfoCache = new ConcurrentHashMap<>(); + + private JvmClassProvider() { + } + + @Override + public byte @Nullable [] getClass(String name) { + if (classesCache.containsKey(name)) { + return classesCache.get(name); + } + + // Try to find it in classloader + byte[] value = null; + try (InputStream in = ClassLoader.getSystemResourceAsStream(name + ".class")) { + if (in != null) { + value = in.readAllBytes(); + } + } catch (IOException ex) { + LOGGER.error("Failed to fetch runtime bytecode of class: {}", name, ex); + } + + if (value == null) return null; + + // Cache it! + classesCache.put(name, value); + + return value; + } + + @Override + public byte @Nullable [] getFile(String path) { + // JVM classpath doesn't have files + return null; + } + + @Override + public @Nullable ClassNode getClassInfo(String name) { + if (classInfoCache.containsKey(name)) { + return classInfoCache.get(name); + } + + byte[] bytes = getClass(name); + if (bytes == null) return null; + + ClassNode classNode = ClassHelper.loadClassInfo(bytes); + + // Cache it! + classInfoCache.put(name, classNode); + + return classNode; + } + + @Override + public Collection getLoadedClasses() { + // We cannot determine all classes in JVM classpath + return List.of(); + } +} diff --git a/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/classpath/JvmClasspath.java b/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/classpath/JvmClasspath.java deleted file mode 100644 index 9ccba83..0000000 --- a/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/classpath/JvmClasspath.java +++ /dev/null @@ -1,47 +0,0 @@ -package uwu.narumi.deobfuscator.api.classpath; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.jetbrains.annotations.Nullable; -import org.objectweb.asm.ClassReader; -import org.objectweb.asm.tree.ClassNode; -import uwu.narumi.deobfuscator.api.helper.ClassHelper; - -import java.io.IOException; -import java.io.InputStream; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -/** - * Classpath that fetches default JVM classes - */ -public class JvmClasspath { - private static final Logger LOGGER = LogManager.getLogger(); - - private static final Map cache = new ConcurrentHashMap<>(); - - @Nullable - public static ClassNode getClassNode(String name) { - if (cache.containsKey(name)) { - return cache.get(name); - } - - // Try to find it in classloader - byte[] value = null; - try (InputStream in = ClassLoader.getSystemResourceAsStream(name + ".class")) { - if (in != null) { - value = in.readAllBytes(); - } - } catch (IOException ex) { - LOGGER.error("Failed to fetch runtime bytecode of class: {}", name, ex); - } - - if (value == null) return null; - - ClassNode classNode = ClassHelper.loadClassInfo(value); - // Cache it! - cache.put(name, classNode); - - return classNode; - } -} diff --git a/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/context/Context.java b/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/context/Context.java index 318291e..5e4f800 100644 --- a/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/context/Context.java +++ b/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/context/Context.java @@ -2,27 +2,27 @@ import java.util.*; import java.util.concurrent.ConcurrentHashMap; -import java.util.stream.Stream; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; +import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.UnmodifiableView; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.tree.ClassNode; +import software.coley.cafedude.InvalidClassException; import uwu.narumi.deobfuscator.api.asm.ClassWrapper; +import uwu.narumi.deobfuscator.api.classpath.ClassProvider; +import uwu.narumi.deobfuscator.api.classpath.ClassStorage; import uwu.narumi.deobfuscator.api.execution.SandBox; -import uwu.narumi.deobfuscator.api.classpath.Classpath; +import uwu.narumi.deobfuscator.api.helper.ClassHelper; -public class Context { +public class Context implements ClassProvider { - private static final Logger LOGGER = LogManager.getLogger(Context.class); - - private final Map classes = new ConcurrentHashMap<>(); - private final Map files = new ConcurrentHashMap<>(); + private final Map classesMap = new ConcurrentHashMap<>(); + private final Map filesMap = new ConcurrentHashMap<>(); private final DeobfuscatorOptions options; - private final Classpath primaryClasspath; - private final Classpath libClasspath; - private final Classpath combinedClasspath; + private final ClassStorage compiledClasses; + private final ClassStorage libraries; private SandBox globalSandBox = null; @@ -30,18 +30,14 @@ public class Context { * Creates a new {@link Context} instance from its options * * @param options Deobfuscator options - * @param primaryClasspath Classpath which has only primary jar in it - * @param libClasspath Classpath filled with libs + * @param compiledClasses {@link ClassStorage} that holds the original classes of the primary jar + * @param libraries {@link ClassStorage} that holds the libraries' classes */ - public Context(DeobfuscatorOptions options, Classpath primaryClasspath, Classpath libClasspath) { + public Context(DeobfuscatorOptions options, ClassStorage compiledClasses, ClassStorage libraries) { this.options = options; - this.primaryClasspath = primaryClasspath; - this.libClasspath = libClasspath; - this.combinedClasspath = Classpath.builder() - .addClasspath(primaryClasspath) - .addClasspath(libClasspath) - .build(); + this.compiledClasses = compiledClasses; + this.libraries = libraries; } /** @@ -60,63 +56,73 @@ public DeobfuscatorOptions getOptions() { } /** - * Classpath for primary jar + * Class storage that holds already compiled classes from original jar */ - public Classpath getPrimaryClasspath() { - return primaryClasspath; + public ClassStorage getCompiledClasses() { + return compiledClasses; } /** - * Classpath filled with libs + * Class storage that holds libraries' classes */ - public Classpath getLibClasspath() { - return libClasspath; + public ClassStorage getLibraries() { + return libraries; } - /** - * {@link #getPrimaryClasspath()} and {@link #getLibClasspath()} combined - */ - public Classpath getCombinedClasspath() { - return this.combinedClasspath; + public Collection classes() { + return classesMap.values(); } - public Collection classes() { - return classes.values(); + @UnmodifiableView + public List scopedClasses(ClassWrapper scope) { + return classesMap.values().stream() + .filter(classWrapper -> scope == null || classWrapper.name().equals(scope.name())) + .toList(); } - public Stream stream() { - return classes.values().stream(); + public void addCompiledClass(String pathInJar, byte[] bytes) { + try { + ClassWrapper classWrapper = ClassHelper.loadUnknownClass(pathInJar, bytes, ClassReader.SKIP_FRAMES); + this.classesMap.putIfAbsent(classWrapper.name(), classWrapper); + this.compiledClasses.addRawClass(bytes); + } catch (InvalidClassException e) { + throw new RuntimeException(e); + } } - public Stream stream(ClassWrapper scope) { - return classes.values().stream() - .filter(classWrapper -> scope == null || classWrapper.name().equals(scope.name())); + public void addFile(String path, byte[] bytes) { + this.filesMap.put(path, bytes); + this.compiledClasses.files().put(path, bytes); } - @UnmodifiableView - public List scopedClasses(ClassWrapper scope) { - return classes.values().stream() - .filter(classWrapper -> scope == null || classWrapper.name().equals(scope.name())) - .toList(); + @Override + public byte @Nullable [] getClass(String name) { + // Not implemented because it would need to compile class which is CPU intensive + return null; } - public Optional get(String name) { - return Optional.ofNullable(classes.get(name)); + @Override + public byte @Nullable [] getFile(String path) { + return filesMap.get(path); } - public Optional remove(ClassWrapper classWrapper) { - return remove(classWrapper.name()); + @Override + public @Nullable ClassNode getClassInfo(String name) { + ClassWrapper classWrapper = classesMap.get(name); + if (classWrapper == null) return null; + return classWrapper.classNode(); } - public Optional remove(String name) { - return Optional.ofNullable(classes.remove(name)); + @Override + public Collection getLoadedClasses() { + return this.classesMap.keySet(); } - public Map getClasses() { - return classes; + public Map getClassesMap() { + return classesMap; } - public Map getFiles() { - return files; + public Map getFilesMap() { + return filesMap; } } diff --git a/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/execution/ClasspathDataSupplier.java b/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/execution/ClasspathDataSupplier.java index 0c1d673..39e7831 100644 --- a/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/execution/ClasspathDataSupplier.java +++ b/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/execution/ClasspathDataSupplier.java @@ -1,23 +1,23 @@ package uwu.narumi.deobfuscator.api.execution; import dev.xdark.ssvm.classloading.SupplyingClassLoaderInstaller; -import uwu.narumi.deobfuscator.api.classpath.Classpath; +import uwu.narumi.deobfuscator.api.classpath.ClassProvider; public class ClasspathDataSupplier implements SupplyingClassLoaderInstaller.DataSupplier { - private final Classpath classpath; + private final ClassProvider classpath; - public ClasspathDataSupplier(Classpath classpath) { + public ClasspathDataSupplier(ClassProvider classpath) { this.classpath = classpath; } @Override public byte[] getClass(String className) { - return classpath.rawClasses().get(className.replace('.', '/')); + return classpath.getClass(className.replace('.', '/')); } @Override public byte[] getResource(String resourcePath) { - return classpath.files().get(resourcePath); + return classpath.getFile(resourcePath); } } diff --git a/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/execution/SandBox.java b/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/execution/SandBox.java index a3b231b..79282c6 100644 --- a/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/execution/SandBox.java +++ b/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/execution/SandBox.java @@ -20,6 +20,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import uwu.narumi.deobfuscator.api.classpath.CombinedClassProvider; import uwu.narumi.deobfuscator.api.context.Context; /** @@ -37,7 +38,10 @@ public class SandBox { public SandBox(Context context) { // Install all classes from deobfuscator context - this(new ClasspathDataSupplier(context.getCombinedClasspath())); + this(new ClasspathDataSupplier( + // We need to use compiled classes as they are already compiled + new CombinedClassProvider(context.getCompiledClasses(), context.getLibraries()) + )); } public SandBox(SupplyingClassLoaderInstaller.DataSupplier dataSupplier) { diff --git a/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/helper/AsmHelper.java b/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/helper/AsmHelper.java index 40797b5..457463a 100644 --- a/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/helper/AsmHelper.java +++ b/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/helper/AsmHelper.java @@ -160,7 +160,7 @@ public static void updateMethodDescriptor(Context context, MethodContext methodC private static void tryUpdateMethodDescriptor(Context context, ClassWrapper classWrapper, String name, String oldDesc, String newDesc) { // Search superclass if (classWrapper.classNode().superName != null) { - ClassWrapper superClass = context.getClasses().get(classWrapper.classNode().superName); + ClassWrapper superClass = context.getClassesMap().get(classWrapper.classNode().superName); if (superClass != null) { tryUpdateMethodDescriptor(context, superClass, name, oldDesc, newDesc); } @@ -168,7 +168,7 @@ private static void tryUpdateMethodDescriptor(Context context, ClassWrapper clas // Search interfaces classWrapper.classNode().interfaces.forEach(interfaceName -> { - ClassWrapper interfaceClass = context.getClasses().get(interfaceName); + ClassWrapper interfaceClass = context.getClassesMap().get(interfaceName); if (interfaceClass != null) { tryUpdateMethodDescriptor(context, interfaceClass, name, oldDesc, newDesc); } @@ -213,10 +213,10 @@ public static MethodNode copyMethod(MethodNode methodNode) { } public static void removeField(FieldInsnNode fieldInsnNode, Context context) { - if (!context.getClasses().containsKey(fieldInsnNode.owner)) return; + if (!context.getClassesMap().containsKey(fieldInsnNode.owner)) return; context - .getClasses() + .getClassesMap() .get(fieldInsnNode.owner) .fields() .removeIf( diff --git a/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/inheritance/InheritanceGraph.java b/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/inheritance/InheritanceGraph.java index 6a1eacd..b2021d5 100644 --- a/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/inheritance/InheritanceGraph.java +++ b/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/inheritance/InheritanceGraph.java @@ -6,8 +6,9 @@ import org.jetbrains.annotations.Nullable; import org.objectweb.asm.Opcodes; import org.objectweb.asm.tree.ClassNode; -import uwu.narumi.deobfuscator.api.classpath.Classpath; -import uwu.narumi.deobfuscator.api.classpath.JvmClasspath; +import uwu.narumi.deobfuscator.api.classpath.ClassProvider; +import uwu.narumi.deobfuscator.api.classpath.CombinedClassProvider; +import uwu.narumi.deobfuscator.api.classpath.JvmClassProvider; import java.util.*; import java.util.concurrent.ConcurrentHashMap; @@ -31,13 +32,13 @@ public class InheritanceGraph { private final Map vertices = new ConcurrentHashMap<>(); private final Set stubs = ConcurrentHashMap.newKeySet(); private final Function vertexProvider = createVertexProvider(); - private final Classpath classpath; + private final ClassProvider classProvider; /** * Create an inheritance graph. */ - public InheritanceGraph(@NotNull Classpath classpath) { - this.classpath = classpath; + public InheritanceGraph(@NotNull ClassProvider classProvider) { + this.classProvider = new CombinedClassProvider(classProvider, JvmClassProvider.INSTANCE); // Populate downwards (parent --> child) lookup refreshChildLookup(); @@ -51,7 +52,10 @@ private void refreshChildLookup() { parentToChild.clear(); // Repopulate - classpath.classesInfo().values().forEach(this::populateParentToChildLookup); + classProvider.getLoadedClasses().stream() + .map(classProvider::getClassInfo) + .filter(Objects::nonNull) + .forEach(this::populateParentToChildLookup); } /** @@ -297,7 +301,7 @@ private Function createVertexProvider() { return null; // Find class in workspace, if not found yield stub. - ClassNode result = this.getClassNode(name); + ClassNode result = this.classProvider.getClassInfo(name); if (result == null) { return STUB; } @@ -310,15 +314,6 @@ private Function createVertexProvider() { }; } - @Nullable - private ClassNode getClassNode(String name) { - ClassNode result = classpath.classesInfo().get(name); - if (result != null) return result; - - // Try to find it in classloader - return JvmClasspath.getClassNode(name); - } - private static class InheritanceStubVertex extends InheritanceVertex { private InheritanceStubVertex() { super(new ClassNode(), in -> null, in -> null); diff --git a/deobfuscator-impl/src/main/java/uwu/narumi/deobfuscator/Deobfuscator.java b/deobfuscator-impl/src/main/java/uwu/narumi/deobfuscator/Deobfuscator.java index 899d57b..bbf8fc6 100644 --- a/deobfuscator-impl/src/main/java/uwu/narumi/deobfuscator/Deobfuscator.java +++ b/deobfuscator-impl/src/main/java/uwu/narumi/deobfuscator/Deobfuscator.java @@ -13,13 +13,12 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.objectweb.asm.ClassReader; -import uwu.narumi.deobfuscator.api.asm.ClassWrapper; +import uwu.narumi.deobfuscator.api.classpath.ClassStorage; +import uwu.narumi.deobfuscator.api.classpath.CombinedClassProvider; import uwu.narumi.deobfuscator.api.context.Context; import uwu.narumi.deobfuscator.api.context.DeobfuscatorOptions; import uwu.narumi.deobfuscator.api.helper.ClassHelper; import uwu.narumi.deobfuscator.api.helper.FileHelper; -import uwu.narumi.deobfuscator.api.classpath.Classpath; import uwu.narumi.deobfuscator.api.inheritance.InheritanceGraph; import uwu.narumi.deobfuscator.api.transformer.Transformer; @@ -48,35 +47,21 @@ private Deobfuscator(DeobfuscatorOptions options) { LOGGER.warn("Output file already exist, data will be overwritten"); } - Classpath primaryClasspath = buildPrimaryClasspath(); - LOGGER.info("Loaded {} classes from a primary source", primaryClasspath.rawClasses().size()); + ClassStorage originalClasses = new ClassStorage(); + LOGGER.info("Loaded {} classes from a primary source", originalClasses.compiledClasses().size()); - Classpath libClasspath = buildLibClasspath(); - LOGGER.info("Loaded {} classes from libraries", libClasspath.rawClasses().size()); + ClassStorage libraries = buildLibraries(); + LOGGER.info("Loaded {} classes from libraries", libraries.compiledClasses().size()); - this.context = new Context(options, primaryClasspath, libClasspath); + this.context = new Context(options, originalClasses, libraries); } - public Classpath buildPrimaryClasspath() { - Classpath.Builder builder = Classpath.builder(); - // Add input jar - if (options.inputJar() != null) { - builder.addJar(options.inputJar()); - } - // Add external files - if (!options.externalFiles().isEmpty()) { - options.externalFiles().forEach(builder::addExternalFile); - } - - return builder.build(); - } - - public Classpath buildLibClasspath() { - Classpath.Builder builder = Classpath.builder(); + public ClassStorage buildLibraries() { + ClassStorage classStorage = new ClassStorage(); // Add libraries - options.libraries().forEach(builder::addJar); + options.libraries().forEach(classStorage::addJar); - return builder.build(); + return classStorage; } public void start() { @@ -115,8 +100,7 @@ private void loadClassOrFile(String pathInJar, byte[] bytes) { // Load class if (ClassHelper.isClass(pathInJar, bytes)) { try { - ClassWrapper classWrapper = ClassHelper.loadUnknownClass(pathInJar, bytes, ClassReader.SKIP_FRAMES); - context.getClasses().putIfAbsent(classWrapper.name(), classWrapper); + this.context.addCompiledClass(pathInJar, bytes); return; } catch (Exception e) { LOGGER.error("Could not load class: {}, adding as file", pathInJar, e); @@ -125,8 +109,8 @@ private void loadClassOrFile(String pathInJar, byte[] bytes) { } // Load file - if (!context.getFiles().containsKey(pathInJar)) { - context.getFiles().put(pathInJar, bytes); + if (!context.getFilesMap().containsKey(pathInJar)) { + context.addFile(pathInJar, bytes); } } @@ -201,10 +185,12 @@ private void saveToJar() { * @param saver a consumer that accepts a path and data to save */ private void save(BiConsumer saver) { - InheritanceGraph inheritanceGraph = new InheritanceGraph(this.context.getCombinedClasspath()); + InheritanceGraph inheritanceGraph = new InheritanceGraph( + new CombinedClassProvider(this.context, this.context.getLibraries()) + ); // Save classes - context.getClasses().forEach((ignored, classWrapper) -> { + context.getClassesMap().forEach((ignored, classWrapper) -> { String path = classWrapper.getPathInJar(); try { @@ -216,28 +202,24 @@ private void save(BiConsumer saver) { try { // Save original class as a fallback - byte[] data = context.getPrimaryClasspath().rawClasses().get(classWrapper.name()); + byte[] data = context.getCompiledClasses().getClass(classWrapper.name()); saver.accept(path, data); } catch (Exception e2) { LOGGER.error("Could not save original class: {}", classWrapper.name()); if (this.options.printStacktraces()) LOGGER.throwing(e2); } } - - context.getClasses().remove(classWrapper.name()); }); // Save files if (!this.options.skipFiles()) { - context.getFiles().forEach((path, data) -> { + context.getFilesMap().forEach((path, data) -> { try { saver.accept(path, data); } catch (Exception e) { LOGGER.error("Could not save file: {}", path); if (this.options.printStacktraces()) LOGGER.throwing(e); } - - context.getFiles().remove(path); }); } } diff --git a/deobfuscator-impl/src/test/java/uwu/narumi/deobfuscator/TestDeobfuscation.java b/deobfuscator-impl/src/test/java/uwu/narumi/deobfuscator/TestDeobfuscation.java index f9dbe1c..b5891be 100644 --- a/deobfuscator-impl/src/test/java/uwu/narumi/deobfuscator/TestDeobfuscation.java +++ b/deobfuscator-impl/src/test/java/uwu/narumi/deobfuscator/TestDeobfuscation.java @@ -139,9 +139,9 @@ protected void registerAll() { .input(OutputType.SINGLE_CLASS, InputType.CUSTOM_CLASS, "zkm/EnhancedStringEncManyStrings.class") .register(); - // Example HP888 classes. Without packer. + // Example HP888 classes test("HP888") - .transformers(ComposedHP888Transformer::new) + .transformers(() -> new ComposedHP888Transformer(".mc")) .input(OutputType.MULTIPLE_CLASSES, InputType.CUSTOM_CLASS, "hp888") .register(); diff --git a/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/composed/ComposedHP888Transformer.java b/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/composed/ComposedHP888Transformer.java index ef3303f..251f18f 100644 --- a/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/composed/ComposedHP888Transformer.java +++ b/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/composed/ComposedHP888Transformer.java @@ -1,12 +1,11 @@ package uwu.narumi.deobfuscator.core.other.composed; +import org.jetbrains.annotations.Nullable; import uwu.narumi.deobfuscator.api.transformer.ComposedTransformer; -import uwu.narumi.deobfuscator.core.other.composed.general.ComposedGeneralFlowTransformer; import uwu.narumi.deobfuscator.core.other.composed.general.ComposedGeneralRepairTransformer; -import uwu.narumi.deobfuscator.core.other.impl.clean.peephole.DeadCodeCleanTransformer; -import uwu.narumi.deobfuscator.core.other.impl.exploit.WebExploitRemoveTransformer; import uwu.narumi.deobfuscator.core.other.impl.hp888.HP888PackerTransformer; import uwu.narumi.deobfuscator.core.other.impl.hp888.HP888StringTransformer; +import uwu.narumi.deobfuscator.core.other.impl.universal.RecoverSyntheticsTransformer; import uwu.narumi.deobfuscator.core.other.impl.universal.UniversalNumberTransformer; /** @@ -14,27 +13,26 @@ */ public class ComposedHP888Transformer extends ComposedTransformer { - public ComposedHP888Transformer(String packedEndOfFile) { - super( - HP888StringTransformer::new, - () -> new HP888PackerTransformer(packedEndOfFile), - HP888StringTransformer::new, - WebExploitRemoveTransformer::new, - ComposedGeneralRepairTransformer::new, - UniversalNumberTransformer::new, - ComposedGeneralFlowTransformer::new, - DeadCodeCleanTransformer::new - ); + public ComposedHP888Transformer() { + this(null); } - public ComposedHP888Transformer() { + public ComposedHP888Transformer(@Nullable String encryptedClassFilesSuffix) { super( + // Decrypt strings HP888StringTransformer::new, - WebExploitRemoveTransformer::new, - ComposedGeneralRepairTransformer::new, + + () -> encryptedClassFilesSuffix != null ? new ComposedTransformer( + // Unpack encrypted classes + () -> new HP888PackerTransformer(encryptedClassFilesSuffix), + // Decrypt strings in unpacked classes + HP888StringTransformer::new + ) : null, + + // Cleanup UniversalNumberTransformer::new, - ComposedGeneralFlowTransformer::new, - DeadCodeCleanTransformer::new + ComposedGeneralRepairTransformer::new, + RecoverSyntheticsTransformer::new ); } } diff --git a/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/exploit/WebExploitRemoveTransformer.java b/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/exploit/WebExploitRemoveTransformer.java index c8f73ad..827c915 100644 --- a/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/exploit/WebExploitRemoveTransformer.java +++ b/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/exploit/WebExploitRemoveTransformer.java @@ -8,8 +8,8 @@ public class WebExploitRemoveTransformer extends Transformer { @Override protected void transform() throws Exception { - changed |= context().getClasses().entrySet().removeIf(entry -> entry.getKey().contains("")); - changed |= context().getFiles().entrySet().removeIf(entry -> entry.getKey().contains("")); + changed |= context().getClassesMap().entrySet().removeIf(entry -> entry.getKey().contains("")); + changed |= context().getFilesMap().entrySet().removeIf(entry -> entry.getKey().contains("")); scopedClasses().forEach(classWrapper -> { changed |= classWrapper.methods().removeIf(methodNode -> methodNode.name.contains("")); diff --git a/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/hp888/HP888PackerTransformer.java b/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/hp888/HP888PackerTransformer.java index a25394c..88d7fa8 100644 --- a/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/hp888/HP888PackerTransformer.java +++ b/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/hp888/HP888PackerTransformer.java @@ -51,28 +51,26 @@ protected void transform() throws Exception { // Decrypt encrypted classes Cipher cipher = Cipher.getInstance("AES"); cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(Base64.getDecoder().decode(key.get()), "AES")); - context().getFiles().forEach((file, bytes) -> { + context().getFilesMap().forEach((file, bytes) -> { if (file.endsWith(encryptedClassFilesSuffix)) { filesToRemove.add(file); - String className = file.replace(encryptedClassFilesSuffix, "").replace(".", "/"); + String path = file.replace(encryptedClassFilesSuffix, ".class").replace(".", "/"); try { // Decrypt! byte[] decrypted = cipher.doFinal(bytes); - // Load class - newClasses.put(className, ClassHelper.loadUnknownClass(className + ".class", decrypted, ClassReader.SKIP_FRAMES)); + // Load and put class + context().addCompiledClass(path, decrypted); + markChange(); } catch (Exception e) { - throw new RuntimeException("Failed to decrypt class: " + className, e); + throw new RuntimeException("Failed to decrypt class: " + path, e); } } }); - // Put all new classes - context().getClasses().putAll(newClasses); - // Cleanup - filesToRemove.forEach(context().getFiles()::remove); + filesToRemove.forEach(context().getFilesMap()::remove); } } diff --git a/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/hp888/HP888StringTransformer.java b/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/hp888/HP888StringTransformer.java index 78452f9..352e00a 100644 --- a/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/hp888/HP888StringTransformer.java +++ b/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/hp888/HP888StringTransformer.java @@ -11,9 +11,12 @@ import uwu.narumi.deobfuscator.api.asm.matcher.impl.OpcodeMatch; import uwu.narumi.deobfuscator.api.asm.matcher.impl.StringMatch; import uwu.narumi.deobfuscator.api.transformer.Transformer; +import uwu.narumi.deobfuscator.core.other.impl.pool.InlineStaticFieldTransformer; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; /** * Strings are encrypted using a constant pool size of a provided class. @@ -33,6 +36,8 @@ public class HP888StringTransformer extends Transformer { @Override protected void transform() throws Exception { + Set classesToRemove = new HashSet<>(); + scopedClasses().forEach(classWrapper -> { List toRemove = new ArrayList<>(); @@ -58,7 +63,7 @@ protected void transform() throws Exception { Type classForConstantPoolType = (Type) constantPoolClassLdc.cst; // Prepare data for decryption - ClassWrapper classForConstantPool = context().getClasses().get(classForConstantPoolType.getInternalName()); + ClassWrapper classForConstantPool = context().getClassesMap().get(classForConstantPoolType.getInternalName()); int constantPoolSize = classForConstantPool.getConstantPool().getSize(); String class0 = classWrapper.name(); String class1 = classWrapper.name(); @@ -70,12 +75,20 @@ protected void transform() throws Exception { methodNode.instructions.set(decryptMethodInsn, new LdcInsnNode(decryptedString)); markChange(); + classesToRemove.add(classWrapper.name()); + toRemove.add(decryptMethod); }); }); }); classWrapper.methods().removeAll(toRemove); }); + + // Inline static fields + Transformer.transform(InlineStaticFieldTransformer::new, scope(), context()); + + // Cleanup + classesToRemove.forEach(className -> context().getClassesMap().remove(className)); } private String decrypt(String string, int constantPoolSize, int className0HashCode, int className1HashCode) { diff --git a/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/zkm/ZelixLongEncryptionMPCTransformer.java b/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/zkm/ZelixLongEncryptionMPCTransformer.java index cb4302b..c53737f 100644 --- a/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/zkm/ZelixLongEncryptionMPCTransformer.java +++ b/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/zkm/ZelixLongEncryptionMPCTransformer.java @@ -104,8 +104,8 @@ public ZelixLongEncryptionMPCTransformer(Map classInitOrder) { protected void transform() throws Exception { // Firstly, process the manual list of class initialization order for (var entry : classInitOrder.entrySet()) { - ClassWrapper first = context().getClasses().get(entry.getKey()); - ClassWrapper second = context().getClasses().get(entry.getValue()); + ClassWrapper first = context().getClassesMap().get(entry.getKey()); + ClassWrapper second = context().getClassesMap().get(entry.getValue()); decryptEncryptedLongs(context(), first); decryptEncryptedLongs(context(), second); @@ -118,7 +118,7 @@ protected void transform() throws Exception { // Remove decrypter classes if (sandBox != null) { - sandBox.getUsedCustomClasses().forEach(clazz -> context().getClasses().remove(clazz.getInternalName())); + sandBox.getUsedCustomClasses().forEach(clazz -> context().getClassesMap().remove(clazz.getInternalName())); } } @@ -136,7 +136,7 @@ private void decryptEncryptedLongs(Context context, ClassWrapper classWrapper) { // Zelix came up with a great idea to infer class initialization order by the super classes. // So firstly, process encrypted longs in the super class if (classWrapper.classNode().superName != null && !classWrapper.classNode().superName.equals("java/lang/Object")) { - ClassWrapper superClass = context.getClasses().get(classWrapper.classNode().superName); + ClassWrapper superClass = context.getClassesMap().get(classWrapper.classNode().superName); if (superClass != null) { decryptEncryptedLongs(context, superClass); } @@ -161,7 +161,7 @@ private void decryptEncryptedLongs(Context context, ClassWrapper classWrapper) { long key2 = matchContext.captures().get("key-2").insn().asLong(); long decryptKey = matchContext.captures().get("decrypt-key").insn().asLong(); - ClassWrapper longDecrypterCreatorClass = context.getClasses().get(createDecrypterInsn.owner); + ClassWrapper longDecrypterCreatorClass = context.getClassesMap().get(createDecrypterInsn.owner); try { // Create decrypter diff --git a/testData/compiled/custom-classes/hp888/IIIlIIIlllIIIlllIIlIllIIlIIIIIllIlIIlIlIllIIIlIIII.class b/testData/compiled/custom-classes/hp888/IIIlIIIlllIIIlllIIlIllIIlIIIIIllIlIIlIlIllIIIlIIII.class new file mode 100644 index 0000000000000000000000000000000000000000..72d6fbd115a69b2f5d60d4ee71b1aa12c4022b3d GIT binary patch literal 1982 zcmaJ?-BTM?6#re4u-mXL=|cD@h(&8nN@)-gEl?CA)nK65P!v(?vTPxA*>pCGo%*D; zRvDeK)3I9Zw9e>*eez?R3E0kb^ybaK$S3Q$_a+3anw{Nq?>XmpzV1CIfBbdl9)JOO z3_9_^pm;e>A@GpE-{E-=HOfjqBiJc5Z$fR=RD~u?Sa3pP#1p(WV$dD9H zq$2Ieyi@=coZJYgv?muri9sK+-Mp6%Mj1khVF4@hg<@ZHnS{d7X-JvHU5mH~3flef}No*2(t=REi(@5ByH~a{023#|<pGZ}6K`HbJjf3t7IQqsM@ubbEJPIdy+Dq!=24LVDTq$9&(qzz|L(=hFtBKz|f{ zc#;ZHT=P<^`J$c5k(RTVb8O!UiW;6`*t_He6LgKUbc@Gq+bI?qIugn37G$Sz*uc|x zCW=8ErC`ok_Sw|rh2v|U9pnpMzXrO_t^WblaEzgm+@%X%F|fQ~x=^6w@7;duum=?1^FmS%%%hNr&P5h!(mFRIPLJ+Y zHt#u8YpV;6U*VY9f^E68mY)|Ik{Ro!p%JFJuf^KM3;~GJypWV8f~3^%l2U(5N_{FR z^&lbZVmfwX4;WTxH%?Uij+(JeH2;DYvrW={W~ZcG=6*>ZF%L+3&`d~r$UH3Rk)H)R zHX%lvGRe9TqLBlfKUBg%b`wM9$gg<54kf&F>@FteHZWPjD^sDtaL-VrJ#ufbJ~XJs zwRruv7;TTlwShrBu6JI%DoAgrzl8Y`-l}ZF+qOONa9lriDaD!6A`#20;L!X8m({PI39R2IzPmIAOjeeR-pjt%Gh$h6)MM-qiY6#7w5yv!I za2l;}(1vB=d+5L_Ax5c^LpXv3*ffVJRV>QdM&Og~Nh(AWan4|gPOA%Zgq@+7x-pA9 I@#y^i2BT>zyZ`_I literal 0 HcmV?d00001 diff --git a/testData/compiled/custom-classes/hp888/com/bric/colorpicker/ColorPicker$1.mc b/testData/compiled/custom-classes/hp888/com/bric/colorpicker/ColorPicker$1.mc new file mode 100644 index 0000000000000000000000000000000000000000..a0a1c69c00418b857dc9877d86e99d6339f6e7a0 GIT binary patch literal 736 zcmV<60w4XJx$hXLNgi>^)cKqhV2{W$Nt-&;sQqUE3Xh~PiROzMPBgsNb^ws~fqoDa zK8j;{V;|po01Ce1us+D8)s5<5fIO{ZB)>)6`yqhJwcSO1E+ijpeQGXu#DE>zRh0d_ z3))vl!Cnb%sO|ASGy11?BMUlT#G|1Zx$!%?ECzjn!km&)l4H%AQ|nxxl4jREW20C8 zI}|YKOKI#MuhB=}Dpj(++L8~Vn<+mD|BI1Tm!wU0+!HL-Gm8QbpL_IGnKIozuX5VW zUlxF>Dc1e?u^X7s2lYRqi0!JO>;p$&x^t_TL&L3Q1}QZix1SmJ0=3;8>5Li=dfRjr zWae{iCz|1~gTNRMq=+DShN+H8VGQLY`D?@@O9jc>q3$~;o>vx~Zq+@Sy@PY`7sB~6 zNt-&;sQqUE3Xh~PiRO!Hc`+wDT?*Uq2>fk<$-)LcUX^#9_bMveG0KIQwsM93hq95l zBMULS|BovqGB+)-)OI(pNQ;DKds9*jWsqE1kCJ^7(A#*;b?+{apNf#HdpZ`rK|JM-JCa$x-n8HN28 zrQn%6px9+`A9k(o4&9Z3a*-{jesLj4tgPBn{X-V%?@|wLC2gp2lL#S@Y%NCwfwGivKEmaBTwU{)$bD6;eEOgZ S>JuqO``f1`M3?mLL8vM*6+P50WK9THjN#F@Y?jb*D~8&3ej*iFI_R_7JU_&-TPE#hcX__)Y_o6t zeA)A5D~XuY8I#t^=(HiGeQ-5oQd#0nT;mogKYF1}gNurC!|i#>5u-dx*3~6U*(_kp zj-HY64L)mv*6?fNLL6rM|MMdL*V$1YrqTJ~d2Y8OtoOH1dl&;lG!t{!cA>>y_-FlgEg~|}d%U0*!?Oaj+bRO|l`XeBP7|vK2mj6PPH!#yLSO4Vi zaJA1ts8l~;lmfP$emBgkPu~q`Ol2j;%e}lLKmtQoOH?+$-Mq4(^J1X|Jzjyu0|Ncy zd}iZfaTt;HV&avX`#gH((b0P)KS|eBUzl7iy?AEj(t30HbzZKURLBK*?H2&b^Sl+E zx$2gP1RicK17A8HEe`SQVA0^r@V^P7i5%}oRsw8h|~J9=q6tPq`+c}NGP z-O8R{@l~9 zv(69-zExlmjZTs}mB)<#MoFnV79Oo-@uQT&qLdxejp9%)(j3GhYob5T|AQR^b;0R8 z9IvdF^EJVok7bsoYk43%rYsxt0s$Ort9m^@ye9aI_oXx3L(weEel^cU@-Cx*W;W5Q zz*)BlKXgy%X);&{zI#_R8!}K}-n>+fX+c->0Xy8k_cr+Q6lO)OHE#zV3KEIlQ#`NFO~ z`5hr6ymRc{bhCp&nKEm=RxkiE6L9G|6_Zin9mZ>b;4f{Lr)tIZ><9oQ^b$6OI)rL} zejVGyBN9%#b`(sU1U*(jdDjhf9mim*z<}=ggU4m3DTVUf2>a9*NS&Lj+?j9+qpWmt zLb!Y&?I?Fs?J_1;!D_agZs><_IRc?&u|CAwju=9sVcM(3^Bv8Vy89_OKwm?(+|vS4 z#qmK#*tMyFU{M(qk}9lu(jm52JhGcDNP-_=#1r{L^|JYToB7vOX)eO~Tjh0KGkgq74mcHO+tV5HVPcYl z9lg<^9_{#2J?B|i5odyP2HKJRiOTq6f-p)ANj|IKYaxIN+39$FyH0F-6biZy)i(bi z`M)qi`j-iVAlS1~Qxn_KL(Ko4FTB?3Y1GW>8$9a~>!T;2W2vr~4U^ z%*7hk_vj>39kl(XKZKCcGg@kd4Q535{83)=pRn}GG#OdtJTUQXyJ|=w3GH2vsK)9+ znwYEuS+y$DqM0N3c=>R>TkH{Ou~TA9Wnw-<8vH8bYZ=Nw>1}rjX!eHph+fW1y*EM> zgj)b?(kp|Dwc=;99an+ua)`D*Bj0V?HNp8;Q}HHEGt)>Xk;U2D37GT@223G!4Qs`r6sNhgsHv^m|h7Ji8QJ8t1g9~ zLuzL+$xBk^+JE_#xS#DNoT-R^>|YyT*IAW@$QagySKz{IKUj^R5aH;_=cOYl-(en@ zm*|qW3xtaL-+ZwHkl5q|<^~VXWenpF5DLB{8jj0OfRO9(oehj-#9T*UFP5fMm!HH_5CIs4EMKEsa% zCKjTu`a7i~n+FuD8H{ACv^_)mtBKmiz8N@2Fb_d~+T`O$D}kUkdQ-%9H1i=)>FaFI z)-bP~2Q`cwgz1Tnf^>C(P^L0<@hX}iVUeW`DU@O&p$a0MOTi4gwOkqlR0Af++v*v< z*Ub*eCbxhxV$-6MDzXmyyTb756TO3+=z#+W9^*DYf9S-?fE=?&}$ z$8Cg@{%54V*Q;`(k?pqWDaMUbIH&C^ZKz#%K8z3Z4;Xht8Uf1Q@$uRRaIpN_naxuJ zbNCYhj1MZnH`4o@Kt%Sr!)(NXpo;s$5k&8j78|1aAp3*NI~B{td_vF?0|a<{l@$fX zwwPXMhv@~lpekq@+l5@}H#4$!Ns&t*6!-g;Vmi&F5n<7fI@Hmqv_D<`P51|UQ1GTJ zoVTO6`g7}v?;LRcD1Evr7~Kuku>a-V2uRrn4E7UMS-Y!AYcWr@IXM6t)n#Q5cTy=e z=<^TnfBDe$SCtR3YlvS#S1{Ewg<}(YGx&_v3MgS8t%W|FW2{ZoV@gr(i(DIb7@22t zg)fCGwyR?p!`c3b>UoXZ{$RfnIWu%gk!Ht5ITWpl;XuUYlldxqbDc~@>5u9s0X;`8 z#ub*SUiN`mGYOd!W&OLY8k*Yl#Bw<`*g<(Ti1C6CpC~o>HilHr zJlPz{1y^=j0!rB-uKrU{GASqjNHHemGVPI3fHi;2T|1v>)FF*|yOfd9MNq{`a#Z#p zyU+cc62LmPYvStt=g$X%r$rs>mN+cr2Mm2?AxK^j{XZNt%IVIM^sG1BG8sB8cC|l# zPP&wnXOyC(=W~84bt!PIHm6)O8D5{kh6>_*}1%?~y&yOfGO4 z4Ki^lr@(GeKg$hcQ9$ez5`4WLdHq9y#C;udl+Sd5xd>LVyuB!aQN^+~Oz*O6lEAct zWV0Cd1bb6^u~tMmOFV}^I3o--8fpI|9G7!u`xnIk=en=@xs@HdP~}F=cGR3pR$>Z#qJwk zzF7B)U1!kXsWqf}!+}9}x*Bs`oXr{pC8> zb8Zsd-fqJUkbX?`1uV=2 z?a{CCR}RadhZF^0ErV)n$I$aJUU@RE=Qr-ysBdn4w(hYRx7BqwTZQ;SZ+x~~_Hw37 zIB@g=!i=yoyHcov;wPjXw-Eb*tuY~nD+dA2Zc1VrGx$J#GggAwQKJF^Ixo=AP?6|N zz6aj*CQ@GW7|R<~zE+~|DoyP9PUFUdrhY~qaX+~3*u_Zq;jVr#%8C;U$Ja`l=w){^ zyL=jvAqQ!`^HL-&YpKyardToiryezP6Poq&l19ayLbVscFE}X)_g+}{I zw_uFU%?-y9fd-L~aGK0%Mh560=~}qXFUigW_pU@FIwh!=Rgb zeDEPAcu`%5>vaI);@%LE(yE!|chd1C%sgs+#|L)|l(KF}PsMn_;wNlAXx-){e1`)E zZr9Md0QU`10n?EDYZ}RxL6-=O*XP{%!n(4@L=CT_+A*}p1iLlFl54oSU&F?*yM6nv zBpE+JeU%xAS!yF0n7i35!W$SY(c@ro8ADN?bR;MI`Ebg`jcD&N@Bpw(y;{v@T zn%+0$_WKuYoY?;|(3Wn1GP7>p-^k3r1}CwXenTFCOOz>*{OP(;jNCqi#zSHvH4mw7 zeq3|>2{7U0Nolww9`NS1fK?zv&7olvKUE!6Q&#l6$;F%3SI-VwVX7K7qO7MKbun;{ zFTPR+VQ((ND;{I-NT)c6q*58ZV(?i+yO(rbZiY^`vzBREYv@LxX~vv2aVHoEQt7R&Ey6KjhDeVXi9k~Ed@fwe)we(9QF^%2u45Sr6~ z#vFWC0?JYb#@H;R8AVFr;1ycf5gWg^0woEf1V)P7%6CnG@=~$LbaSmRwDNuHvO+f5 zdklaD-f!^+G<}yMJ~p~WErlK+v+t`{OZ&ANdeq8=4=B#0wzc3=R(FF$gxhJ2ep0@i zQ8voqNJ~fmyV}{b_ZXcFPRM2UusVvwZ{4nzqV*m&PZ34KXL91GD#^LW5|H!Ozn&Et z>3l-ZA%>;WZPIcrLf(Fxy;`4)YNOBOGI+jPB1mEUZ;Rj&FWP&4G9Qb|q~&_)$EObYHt zvK+`=@BWvB)?voHV#k+0{%V)Vm)=sH=U;@VVY#SPtPfzdqP7}s>$_d7S#KSVKl|VO zDqW0&Z!M@^s-gpf+klLh|E>@7tre}yK&81^Ek*vEGk)>$&rQ@u`*Iuc674(BiW7&y zD;@dD2)_5WH=>93PDbp%`B-L950@16syFB$UG+b%d-$&(+j;0bb%ZRQ;Jd#|RC~an z(_SzmHq?|+O!rz-wOThQG_f+88=i=foTd7~8?c2^3L9)XBVpsIG_;sLBTI3GB9r#~ zNO=Pq04`H4{Z#lzt|fvrR%j`>J$x#_1L4YP_}_S-c;Nf4+m9acx?-q}WpQrK!m^+^ zp|B;X(nqqz)5*jSF$@H#F`}O`WkPx@nG34{Fz*3kNs{R@@{Vy180Xs7dkFf@@_XO{ zL``O^Wef1A@m`P@ai7__rW=eXU+uWGN(XqNf>@M z${Z>v_q^2*Eut#1OA|@xzlV^UkcASyS_DKSs~~3BkQx*Ax`QPC670S|0zN6mVLXg- z0JBd(2im6Eijy@^F%@(ISC*s9{sBvj#_fVHQQ1wcWK7L*++St0Jo3qmm<~Byu zK5}3#JH8SOm3g@Zz|aulfTukXGHh38h{~M; z7d-zG_ZopsShPvK9t=^`tKppHI2j3e@02mOpkcEA?MNPa=#Ap+t?GH0V*VMqt{Jg@T%S~{E#B0$!m5aPxVGGuv3Ms) z`+iLCWK9;qQ&$<~_itYi!~MoT(yHqjHdq#Ul-HRWW^8fui10lKpDTGL?-`~f;WEq~ z=Mn!JeY~a^X_HlqQkGSPdFhi*q)IrR%E_!avN)pDJGHG5*TCgM;X8TsGX3&Kt z(jRtMu2e*&Preuld%b1yj&=-XYDN3Fxw)m>c}SuDNZ4Vx{ZrRRk{XGo!L2{$fIg3< zgM*YVVBRe$mct-W5(I*nAGX)hv!1lsCs>z5z>tR~{73x66R*9b&BtMK`o3UKDU_yl zSFyI|zx~+Sy$0DQFpaU~V3$O;phF-cSNW$*(?B69D+0X3r!_1T*&sK~Hg^4qN!Xjc z_>0QL6d784oJD~6R-yL8Z@~M1-%lC1D#m~jUih7_cZfEPh>EqhR#6d3EtLSvzZorz zUTgT$v8r?TB#D+@N$J>Nrj2ZYb~$@85g2&dGToDgT5vWwp~@s#{JZ zEA9?F09MCSctfLECuw;Pw^R%ObtTqIaI z@|y=Kgz!wp(Dpif755Lk4-LlrHX*OK{V0gZIhY{7J9_vgF^;Ou`Gd`ZL+;At9rQ>MjQASq^~Q3T8Yd!z8j+2&DUA%`W~-Ds^!9G8UX^lBs4p<OM7++h&v?s7o)!~2cX9B87-1UpHsyN;|QJ6qziZY4f4(Rp+E z+;7s~@4B7c9aci5gd4qUm3f(yy47@V3%%~>%+8WmJRe&9Q_6d0c5X~r@5kREB(NU$ z-o7tgT#WGBLC6?e39D!b)_ya6fu3jf;P6am7-Qx8w~=g zomfAwd;QZW zqx2>%UXg}c;u$}9FM5*0M~?pj#fCm{hK6--R23u3zlKZkZUW=afn93VMNW73z4zy; z=pjv6d>Mhw?({WY-Bcd|VZmwj;D`fBzSV+mKK}i7-zo#8cTrZtR#;6OD=GnOBovi4;%y|* zjMB%U3;Z2wl$AT7)@4US12=fg5qgXD?KfB8U{4ouQh^s0%_)4+Q?>-sdulO-`XwId z;cSeD$9_nSN5m(V?1nCt1sDDm+&P+X`JB5rGZfvC!trR#D|^^IK-Pkz#RYQtP8gxn zJZ(R30(n7UQr9@{Z+=1y&*>;4qepr@hwOq{1x2qPq9 zI1;dwN}9pi?gleQRwHK+!gWu@8R`9lze5as=E3U+H_|14ePFKcbuPp)>UD z<9!wQqU~QaqCQXT)oj#q>|J#5k=(K~Vtu0)@Dq&=Kyp-hzlA`phopK(Ez&Kv3N z#Ko#5-<1*cW*YC5PoccbEQgBi3+A#XK8U>f?&Z5_hH{bgfoX&ns?F9t`>1?Y+fk5= z3burb1QjSe773muP~O4IG863d{_e}(C`ZGyKFn+-&V2o%Vylqn2Up7w(AY#oPS>To zJ5J60?LK-Q24n_DXGGfhB#?QZxz_h1bkCF9h{I9(7i^ljHk5Jw?l+Ln%HxHpSAoXV zFnx87K;ldQ%>iYDMIk7UiiMeA_FJxRqrPD{N@+d%bKqq8R#R}-ugM&on zX&thBJ-WsJtUU;u_xN=ipK=5cCEKWh~aXB{lm-$r+f)1W61r6U+p`k zCM?hH3Le~bY*(|Mg8Z*vy16!a1Tsn6p$oy6V2~>% zR(ZZhZ#%5}#3gby#G&geO4RywJP~l)@8F#slCu_-h~VCZ(tGrMsrr7u65Hb}J0DDo zHW~OkBDAmWa#-v9EJdu@lhkx#@T@4Ar-?UvI#Bz#3!72+d>m<{p7 ztyicaHEni%FXZ`B`=j?p#mGvMA?9M>`t9|6+H%RvV2C^wZ%=1WkZFTmSqe=u!mcUn zv!t3oaRbVIv0HFOPbCh20*aA~;BMPJqyqllCOZj1boxwBQH0k1z@07u%qm0MhBpXI&wjeYQ(5m(kUC%lX4o zY)afT);DH(HCU7>l+Ry{Y5yoKyGP_X@-FXDr2Ar|e7b=SpI9QQgID5>${`ILKC)xq zjt*WZgHSO!t#iv{w@u$~P6e%wko;e^|Kx08LY;+qMAtal?_Wu--UGoC6}`=h-NTz0 z2vTRDYYiq8)Vghq4C3Twr%}|ki{eIVp$SSy-t5LP%;IYUL*(5@yk`PBj~Fhkqh))I zKa~ivES;=x?>llO+Dt6;Zkm$h3gET`3~J*@G$VSn{EsQLe}P5}a)S5r!1=A>F&N62 z;SvD$wA?h};9$C}c0Epn*JgZsfYe?lGx3Wu%IX@fF-0(%pZsy15{2Q}Kixd_sHmc^&{C2fZPKv>+tuVgmDw54hLkuW$$Ud9$JeQqTx|LE7?SHw`6 zzA;K65S;>usrA%!3HvfNSwEp8Eeu z6X>5B`JdLVcz9iyhi9)^O7=R7;?2ZE_kneq71(VU@r;cQQufYr*>DJGs$ukZ7AEOZ zyN*3yyG5>}Co<^O5MFhP7W!EN?GbrShth!*iF~c{&!cE1I z1cUf>DLs2)6yH+0aCg?zj``#4Su&h2?^Ov^yvKlZLaT2{az_ky0}&7NcMB;$eJ!q3xBdjVMRlpK02E>BaHN5Tel+d-Y%?&-Z~lbZ5L#p5dj#*Iol$w+Qtwa zwKgKxqTDoi*Hx2^(+N6)xM6ALeNiTlfRj>$kR81;jgHw^wCxQXh-81(5oh8S`i2x* zHB|KdXO^)~^uNw?elL*5sNiFblHlA0{6P_jIP3Aw>~tPk%KlJRjKn7~y=?YD_8a(# z`mcAq+aEO?EIAwWHk%G^Ix4{nwic&*kah6(`=WK+wXJsXx|iGrxW0F>0lizZ>SBBN z58-a$;QgZ0?FTu@hi@kXiZqs)JUKX1uD99*ro2ozzI9UbQtK7ZkY22nJ;yw3L93B+ zEB)rI*PQ5};^b-lvZlP`d2}k{l%#LYzwG<$OPHS+Y~-J~RIZg?mw&il=2?CW%8s>Y z(HoI{C0P^y{%A{X`conYo@+$Re$Z>RLB`3c4yBXw8FSe7S|&U6iJ|4hWw3@y@Es;Z zgSrZHE4aWQOk#IaZ{6AqL0+Tp2H7!LeL&@>@os^vpXsbk7Ww~FhI{R97IR>RDZ9%y z$0~P@^u2TbriENFlNKp-6mcRgraxj>=YO_atHmjxg?MiybraDC-iI+*ASFF{AuEs~ zOlT#=Bvb0uY<|rn_PikS*qz9#kBnM9r-_B+28B(NJ%%1dG+xC_W$zb;X}_sB27Myp zft>wu(-WUR_M2W<`^IHMtjlRJqFzo}cu=@-Ee0KJ)>V-FQ6mYs{946)&yoH?T<HI1M^WRhP6c63U9-64^sJe1FTNeQ zA&mNT1$T_EK(MIM#TPpX{p=q7QcuMO1RNbYy|un>P{PX%>X!g{*s|nw&BF#cH9DB| zc!OJ^%yjO_TozK$4R03k*}zmHeS{Y>vy$etYJSEx7Q+kh*k3>&fYx%A@k*r=@85J~ z9FpGgt)fo%+H*0M|Ayf<0paF4CvE$pljje}4qKDsAh@3O1A=`Y)I7!*gYiS~;*E2s zM<&n8X9ROOG7NiaEs>(gctCtMKv%a@2=I#B?W*hRkWc{2k#vKAq#lNQ@#~EB*|6@)zxO-o{Xk z626OcJb8{?rZzCdR;!Dc=;xjsWm@y_*TM259F6VPu7MXY(Mw+s< z(8*~h^SF8HqGQceJ75eWE@t%fD589+_RJ9| zJ;l;y+<4E`N+7g>5Q>*xpZ!?-sN5vK`z7UTZ8yGFE7#R6d^`X}gF`6mAt zI?kn$nY~V^^5VrmxN!~o%QaxIk&1?`+*OzRoiMwHPxvjX=}%#eQr+M zsh7gm#U(-4l@|6_*5W5U#13-?Qz^1z#XDOo`0}qMNzpB`eK!}OO{#Ijeo4@leAq3W z^NGHrT1=|IS#$1RmBi05{N``^JkuD;{i{oP!*3@)EBv9mid}5SoQ05_afkK4S}o8! zBoOCM*^@?c*Ztcyjl`~;0CJs0f|E+YQ(F@yL`n}h7`s_Y=Z-pC%+nr7{>n(xT1}Y@P`yFG9Y(4 zPxG}imK}t94hU|0%95E_?Usdg+)OF&C5$Gawc=)y(@h~E!*xipg2Xy7MBBsG>Hw{E z*s~vl--W}yBtCOHNN+S$9LuZ+k#&-=Q z=^}D(1nnM^?<;X@s^d*YIgsQIkfV`FQ*P~xs3Fd@dd9dOJ~@vG0cQDIJPjYgi zGmgy%?sSDEjuuDKJEyr~E8TUt{q~%6Z~T9!v)CxyT~r##Z22CorgpUR3((m4W~iPl zHv5bLKDsLu-u!i7AW!L>XGqE@W{IZCPsiOhFR>n-eriWd={tZ}#uSMh-{9i;@E!K} zTs9%d3b6`;bB^QJ$*iAm)?@0S#wGA?q`X04fDL2x@LT3dFpoSx)Be5r|MG%?67&Q`n}HcL8NfsTrlNV4v&^$#T2I_0u& zY3`z*j!X0bGnh#V9u-n-=vJTCV=r+u=F8>3sY5HUo0E1+;I?1kMQzHW` z&rh#1IQ9G5WGQQunivjmcp+nuxYub++*C0RvX1dgx;a!C2Km`x$QN!E8k${R_^mul z4|Pm6@hTa*v;Ta=QpU+s_`fNoThKJIq)JN_9GLg{{u0#?UA;2PH~@!$)IEI|z%&(( z>A#dq9V4~u8i~e|HQFub{<%J z1tz2Jmh*J`*ny*K%<;Kr3ECLJ_-D#S3B66q`yUIpNSyNp5?hwgt~Y&Gm{o^+=qW^m zLN=6W7(vkq($0g&>`bu1A3Fo9U7dzBdlnpJB`WYjhH9CXw4NHpeJSdD?L1SE1gZ?^ zJd5c%y^e(J4Cv!dh@pSh4cj~P}tP?^@BuI(HWk`Dhx4@LjILa5-}`9yQ09M~PS^H^5XOg|E#qPSfU9Mc5G(af3wf z>XfvlsoJPJ^Etft9V@R&HDdFaOrU;$PqY9h2mpB5VxEZ)&|P&uELda{r!p3L=fJ{6LR_~Zm5Iy|+x9~L6` z2MCC<{Ck;j4E5Ne?*Wr_3So^|s__&IMSr7?eeTOc<%RAGaKJz6CjlKoLFg+>{{wiM BMjZeE literal 0 HcmV?d00001 diff --git a/testData/compiled/custom-classes/hp888/com/bric/colorpicker/ColorPickerMode.mc b/testData/compiled/custom-classes/hp888/com/bric/colorpicker/ColorPickerMode.mc new file mode 100644 index 0000000000000000000000000000000000000000..c5f8c03e874f0bc549b2b5c848d306340dfb47a8 GIT binary patch literal 1120 zcmV-m1fTmw&95|3o`Dmc@G4U8F(ykgNt-&;sQqUE3Xh~PiRO!Hc`+wDT?*Uq2>fk< z$-)MJ==@+LrL0>=x~>T?QJ3^e-Mw6?yX_Kh=}1qEUM4VQc{YpuZX+}&@;!Y-6)AGy zt*lEiI3|C}yLDtlAx@SD>K-&ruQaoC?>|Cm1B4!RO!-Kuc42N%i`)}x2h@o+mKAvX zw~Q@CurG}qJN=4yzGB?GOrSWpUf9&jq=DA}RDM`=vz-9hm+hh?aFJ@Dg^3qo7LA|985`xtWyT^kP8#`fM*xkD$C(v>BASNNSK6yv9r#eRX%wae-YPZ zZL(nJ4Jh`KwvnP$NgNPqR@3ti1XM=c_tBb-k$X;G+y+x9CMpo{M6-LEmJ+{D3aO`m ze>P5=2u2)QW2h3x@RG1ZV3h4?t2I?BBmkA#4&39uKm3!tn`?&2W zw<3ZAcQHGbW8C#O%XflR&vcY@o0mA)Uk#)7sQPT_Kh~u6F)iE^(8Bo(GvVZLT~;In zM(Wt%#(IUpuGH_@G!f8xUnzXwgkT@iRPw<8GVWsE5KGh;={AJ>R4e zBPM;}isO8SUytw9&r=_u8hE>vR1^!B9Ux?kXQkXIZ!Ao;9X%6I1@CQ?>ep`)2Q|>@ zJLx*{cv4@!km&)l4H%AQ|nxxl4jS?veE*+VjF|(2FG>q#iv3tbq&RkF1l(^ z=Eu9^B#I@7Z{>2_B(#(_m!l%3ftVf&`T7xeJaI+kbiD*gGWy(>q9Gy3n$ktHx@hCw z=13?nNz~4!bKJpu>Kvz~~K zXtn=2rS@{?;MDE~)e@>Oa|bp#66u#T0!Er&3<N$Z7E+s3u znjkWSLm6B3f2{3D{y4>fD7Gd$8FDm=!KS-a1tr#2cRp%q9!>=SbEX+#e<(8b<=9!- zjqgn|6w6mJ$GaDGZ$E`dGF`bP?A)Zizzw-@F>>z%E>BYvM-@0@v=&{h5uDM4gGf$e zlDiKb4vlYiBu*Es!5+iJO}>&NOfG?1k{U5yk7(p6@X+?oV@5_sZ>fzY4u_5@3l@(< zE5X@iS&elKd?tSvfX@W{ZX>F}=<~F|%LjmT_1|AKroZu<3;UC_(?Q0ipRl7Lf z_PoYL(fMFbWE&VbK21ECzG&tlHGQKj mhE?_%VBH`H(Q~Qo{ZTTc^F5dKatoMl-kw3Jc-1uxiAu!2~Xo7M_iESIE6LU=GsS!r?ElHEmuPmSRh z_!E5bRbx_65>j9N5B?Gpan6=Y3F;=hXU?3NZ)U!kvp;@+I|ML{48w4xq^ET~O|N*; zB#~UzI7;ymDq$i{g$)N|XkJ&}sBv92*5Y$3>srp{a5IFDaRiKGTlFC?27l(vC2N}n zV{MciPQV5FX#^oLNMkv@U=-{L26rU7$lzuGYZO#?W9FQ+d@)nx?wL z;E6<+QwrJ}eZ z=G5i<;i!VkxFVwq-NfjontL6e*-7cDWo3%$s%COrWeEMlnd3TML8in zqDhG?r=p7@>BmBoVsI^|I3^h`|36iZr!+1Cj_Pz0oKZIy1RK<+Ou^7*w>DNZ)6sxX z;h~G`LRd^GNntUVjz)rnda_8JvG}{&=Zti?0VQPXCIHgA0V_8+ryJj6xxMp`gX!~MDrnWggB-c$1{4T v=r@C3Na8tJ3{l!Z6IJd*0L^p@{OBQR1g&Hd#5~&YlE~NS-sMSepab{=Klm=X diff --git a/testData/compiled/custom-classes/hp888/pack/MyClassLoader.class b/testData/compiled/custom-classes/hp888/pack/MyClassLoader.class deleted file mode 100644 index 1e4a27d0bd2a07d05bffe3b0c583ca97448cbdc2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2053 zcma)-TUQ%Z6vzK36FMXeB@7|Tr4_KnB-|{57m{Ktf*s984Wb2aLl{GFaxs}yd1@DH z`Q#_4pQ1%g)>4WOyC+Jr47#y_zMM+-+0yB7?>|y`&RnMax;NHzrMLhtLtb18r3%=@ zR1y@p98Gno=A2rwTu8*aOwx^FqW{M)8DAyWiH@L*&QzR@N~65D{#D+pGHn%u8hQ~3 zz=t7$&{^;*h6Q@eYSli}@Dv7uh~Q}r=eg&{s9addjb|;V$+PA;)3Sum7Vj1{c-G9w ziA}eHB*h%1%#)jtLrwzN?S!$+taqjGNDA~O^LDXZbXEnFXlzTvIjB7E%gXlee_T<* ztx{IW9DH+Ixt0HONf~MVHl*Bs^yo!_b4f33IQrzyWT81+VjK$sLml9o`?boK=B{n2 zxDv!7kMqvJrJVzBT3uvV7 zuCiGo@o=)4&LPx zkdp|g1g{JijeIibT}GR^{c*Xlqa?<0Uf1=Oj&vi&0{*qi9xYtIT;8-<-Q?XPFpw&i ztxTh|Yt=l9o~qgSkqhmbW&FoTV>g z)t$7(S?ZxlIu?HV5AcVAd`f^29Ula!^&yBpYW>jAPaaC>I=>B|-~)R4n&K2{$K&54 z{17@fGfj-7XCI2$h M?N6y4dHhe;-wDVcU;qFB diff --git a/testData/compiled/custom-classes/hp888/pack/MyFunction2.class b/testData/compiled/custom-classes/hp888/pack/MyFunction2.class new file mode 100644 index 0000000000000000000000000000000000000000..aac59240a04e79e2a036b37c5c22d18b0df844da GIT binary patch literal 4792 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 ala$WqK3b_qg22tm%fg5B9&!5PJ|6+2<4aBe literal 0 HcmV?d00001 diff --git a/testData/results/custom-classes/hp888/com/bric/colorpicker/ColorPicker$1.dec b/testData/results/custom-classes/hp888/com/bric/colorpicker/ColorPicker$1.dec new file mode 100644 index 0000000..30f70fc --- /dev/null +++ b/testData/results/custom-classes/hp888/com/bric/colorpicker/ColorPicker$1.dec @@ -0,0 +1,36 @@ +package com.bric.colorpicker; + +// $VF: synthetic class +public class ColorPicker$1 { + static { + try { + $SwitchMap$com$bric$colorpicker$ColorPickerMode[ColorPickerMode.HUE.ordinal()] = 1; + } catch (NoSuchFieldError var6) { + } + + try { + $SwitchMap$com$bric$colorpicker$ColorPickerMode[ColorPickerMode.SATURATION.ordinal()] = 2; + } catch (NoSuchFieldError var5) { + } + + try { + $SwitchMap$com$bric$colorpicker$ColorPickerMode[ColorPickerMode.BRIGHTNESS.ordinal()] = 3; + } catch (NoSuchFieldError var4) { + } + + try { + $SwitchMap$com$bric$colorpicker$ColorPickerMode[ColorPickerMode.RED.ordinal()] = 4; + } catch (NoSuchFieldError var3) { + } + + try { + $SwitchMap$com$bric$colorpicker$ColorPickerMode[ColorPickerMode.GREEN.ordinal()] = 5; + } catch (NoSuchFieldError var2) { + } + + try { + $SwitchMap$com$bric$colorpicker$ColorPickerMode[ColorPickerMode.BLUE.ordinal()] = 6; + } catch (NoSuchFieldError var1) { + } + } +} diff --git a/testData/results/custom-classes/hp888/com/bric/colorpicker/ColorPicker.dec b/testData/results/custom-classes/hp888/com/bric/colorpicker/ColorPicker.dec new file mode 100644 index 0000000..7a21b81 --- /dev/null +++ b/testData/results/custom-classes/hp888/com/bric/colorpicker/ColorPicker.dec @@ -0,0 +1,418 @@ +package com.bric.colorpicker; + +import com.bric.colorpicker.colorslider.ColorSlider; +import com.bric.colorpicker.colorslider.ColorSliderUI; +import com.bric.colorpicker.listeners.ColorListener; +import com.bric.colorpicker.listeners.HexFieldListener; +import com.bric.colorpicker.listeners.SelectAllListener; +import com.bric.colorpicker.models.ColorModel; +import com.bric.colorpicker.models.ModeModel; +import com.bric.colorpicker.options.AlphaOption; +import com.bric.colorpicker.options.BlueOption; +import com.bric.colorpicker.options.BrightnessOption; +import com.bric.colorpicker.options.GreenOption; +import com.bric.colorpicker.options.HueOption; +import com.bric.colorpicker.options.Option; +import com.bric.colorpicker.options.RedOption; +import com.bric.colorpicker.options.SaturationOption; +import com.bric.colorpicker.parts.ColorSwatch; +import com.bric.colorpicker.parts.HexField; +import com.bric.colorpicker.parts.OpacitySlider; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.util.ResourceBundle; +import javax.swing.ButtonGroup; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JSpinner; +import javax.swing.JTextField; + +public class ColorPicker extends JPanel { + public static final String MODE_PROPERTY = "mode"; + private static final String SELECTED_COLOR_PROPERTY = "selected color"; + private static final String MODE_CONTROLS_VISIBLE_PROPERTY = "mode controls visible"; + private static ResourceBundle strings = ResourceBundle.getBundle("com.bric.colorpicker.resources.ColorPicker"); + private ColorModel colorModel = new ColorModel(); + private ModeModel modeModel = new ModeModel(); + private ColorSlider slider = new ColorSlider(); + private Option alphaOption = new AlphaOption(); + private Option hueOption = new HueOption(); + private Option saturationOption = new SaturationOption(); + private Option brightnessOption = new BrightnessOption(); + private Option redOption = new RedOption(); + private Option greenOption = new GreenOption(); + private Option blueOption = new BlueOption(); + private ColorSwatch preview = new ColorSwatch(50); + private JLabel hexLabel = new JLabel(strings.getObject("hexLabel").toString()); + private HexField hexField = new HexField(); + private JPanel expertControls = new JPanel(new GridBagLayout()); + private ColorPickerPanel colorPanel = new ColorPickerPanel(); + private OpacitySlider opacitySlider = new OpacitySlider(); + private JLabel opacityLabel = new JLabel(strings.getObject("opacityLabel").toString()); + + public ColorPicker() { + this(true, false); + } + + public ColorPicker(boolean var1, boolean var2) { + super(new GridBagLayout()); + this.initNames(); + GridBagConstraints var3 = new GridBagConstraints(); + Insets var4 = new Insets(3, 3, 3, 3); + JPanel var5 = new JPanel(new GridBagLayout()); + var3.gridx = 0; + var3.gridy = 0; + var3.weightx = 1.0; + var3.weighty = 1.0; + var3.insets = var4; + ButtonGroup var6 = new ButtonGroup(); + Option[] var7 = new Option[]{this.hueOption, this.saturationOption, this.brightnessOption, this.redOption, this.greenOption, this.blueOption}; + + for (int var8 = 0; var8 < var7.length; var8++) { + if (var8 != 3 && var8 != 6) { + var3.insets = var4; + } else { + var3.insets = new Insets(var4.top + 10, var4.left, var4.bottom, var4.right); + } + + var7[var8].addTo(var5, var3, var6); + } + + var3.insets = new Insets(var4.top + 10, var4.left, var4.bottom, var4.right); + var3.anchor = 22; + var3.fill = 0; + var5.add(this.hexLabel, var3); + var3.gridx++; + var3.anchor = 21; + var3.fill = 2; + var5.add(this.hexField, var3); + this.alphaOption.addTo(var5, var3); + var3.gridx = 0; + var3.gridy = 0; + var3.weightx = 1.0; + var3.weighty = 1.0; + var3.fill = 1; + var3.anchor = 10; + var3.insets = var4; + var3.gridwidth = 2; + this.add(this.colorPanel, var3); + var3.gridwidth = 1; + var3.insets = var4; + var3.gridx += 2; + var3.weighty = 1.0; + var3.gridwidth = 1; + var3.fill = 3; + var3.weightx = 0.0; + this.add(this.slider, var3); + var3.gridx++; + var3.fill = 3; + var3.gridheight = 0; + var3.anchor = 10; + var3.insets = new Insets(0, 0, 0, 0); + this.add(this.expertControls, var3); + var3.gridx = 0; + var3.gridheight = 1; + var3.gridy = 1; + var3.weightx = 0.0; + var3.weighty = 0.0; + var3.insets = var4; + var3.anchor = 10; + this.add(this.opacityLabel, var3); + var3.gridx++; + var3.gridwidth = 2; + var3.weightx = 1.0; + var3.fill = 2; + this.add(this.opacitySlider, var3); + var3.gridx = 0; + var3.gridy = 0; + var3.gridheight = 1; + var3.gridwidth = 1; + var3.fill = 1; + var3.weighty = 1.0; + var3.weightx = 1.0; + var3.anchor = 19; + var3.insets = new Insets(var4.top, var4.left + 8, var4.bottom + 10, var4.right + 8); + this.expertControls.add(this.preview, var3); + var3.gridy++; + var3.weighty = 0.0; + var3.anchor = 10; + var3.insets = new Insets(var4.top, var4.left, 0, var4.right); + this.expertControls.add(var5, var3); + this.initializeColorPanel(); + this.initializeSlider(); + this.initializePreview(); + this.initializeHexField(); + this.initialize(this.hueOption); + this.initialize(this.saturationOption); + this.initialize(this.brightnessOption); + this.initialize(this.redOption); + this.initialize(this.greenOption); + this.initialize(this.blueOption); + this.initializeOpacitySlider(); + this.initialize(this.alphaOption); + this.setExpertControlsVisible(var1); + this.setOpacityVisible(var2); + setOpaque(this, false); + this.setColor(Color.BLACK); + this.setMode(ColorPickerMode.BRIGHTNESS); + } + + private static void setOpaque(JComponent var0, boolean var1) { + if (!(var0 instanceof JTextField)) { + var0.setOpaque(false); + if (!(var0 instanceof JSpinner)) { + for (int var2 = 0; var2 < var0.getComponentCount(); var2++) { + JComponent var3 = (JComponent)var0.getComponent(var2); + setOpaque(var3, var1); + } + } + } + } + + private static void requireValidFloat(float var0, String var1) { + if (Float.isInfinite(var0) || Float.isNaN(var0)) { + throw new IllegalArgumentException("The " + var1 + " value '" + var0 + "' is not a valid number."); + } else if (var0 < 0.0F || var0 > 1.0F) { + throw new IllegalArgumentException("The " + var1 + " value '" + var0 + "' must be between [0,1]"); + } + } + + private void initialize(Option var1) { + this.colorModel.addColorListener(var1); + var1.addSpinnerChangeListener(var2 -> { + if (!this.colorModel.isChanging()) { + var1.aboutToChangeColor(); + var1.update(this.colorModel); + } + }); + this.modeModel.addListener(var1); + var1.addRadioActionListener(var2 -> { + if (!this.colorModel.isChanging()) { + var1.aboutToChangeMode(); + var1.update(this.modeModel); + } + }); + var1.addFocusListener(new SelectAllListener()); + } + + private void initializeOpacitySlider() { + this.colorModel.addColorListener(this.opacitySlider); + this.opacitySlider.addChangeListener(var1 -> { + if (!this.opacitySlider.getValueIsAdjusting()) { + if (this.colorModel.isChanging()) { + return; + } + + this.opacitySlider.aboutToChangeColor(); + this.colorModel.setAlpha(this.opacitySlider.getValue()); + } + }); + } + + private void initializeHexField() { + this.colorModel.addColorListener(this.hexField); + HexFieldListener var1 = new HexFieldListener(); + var1.setColorModel(this.colorModel); + var1.setHexField(this.hexField); + this.hexField.getDocument().addDocumentListener(var1); + this.hexField.addFocusListener(new SelectAllListener()); + } + + private void initializePreview() { + this.preview.setOpaque(true); + this.colorModel.addColorListener(this.preview); + } + + private void initializeColorPanel() { + int var1 = this.expertControls.getPreferredSize().height; + this.colorPanel.setPreferredSize(new Dimension(var1, var1)); + this.colorModel.addColorListener(this.colorPanel); + this.modeModel.addListener(this.colorPanel); + this.colorPanel.addChangeListener(var1x -> { + if (!this.colorModel.isChanging()) { + int[] var2 = this.colorPanel.getRGB(); + this.colorPanel.aboutToChangeColor(); + this.colorModel.setColor(new Color(var2[0], var2[1], var2[2])); + } + }); + } + + private void initializeSlider() { + this.colorModel.addColorListener(this.slider); + this.modeModel.addListener(this.slider); + this.slider.addChangeListener(var1 -> { + if (!this.slider.getValueIsAdjusting()) { + if (this.colorModel.isChanging()) { + return; + } + + this.slider.aboutToChangeColor(); + ColorPickerMode var2 = this.modeModel.getMode(); + switch (var2) { + case HUE: + this.colorModel.setHue((float)this.slider.getValue() / (float)var2.getMax()); + break; + case SATURATION: + this.colorModel.setSaturation((float)this.slider.getValue() / (float)var2.getMax()); + break; + case BRIGHTNESS: + this.colorModel.setBrightness((float)this.slider.getValue() / (float)var2.getMax()); + break; + case RED: + this.colorModel.setRed(this.slider.getValue()); + break; + case GREEN: + this.colorModel.setGreen(this.slider.getValue()); + break; + case BLUE: + this.colorModel.setBlue(this.slider.getValue()); + } + } + }); + this.slider.setUI(new ColorSliderUI(this.slider, this)); + } + + public Option getSelectedOption() { + ColorPickerMode var1 = this.getMode(); + switch (var1) { + case HUE: + return this.hueOption; + case SATURATION: + return this.saturationOption; + case BRIGHTNESS: + return this.brightnessOption; + case RED: + return this.redOption; + case GREEN: + return this.greenOption; + case BLUE: + return this.blueOption; + default: + return null; + } + } + + private void initNames() { + this.hexField.setName("Hex"); + this.hueOption.setName("Hue"); + this.saturationOption.setName("Saturation"); + this.brightnessOption.setName("Brightness"); + this.redOption.setName("Red"); + this.greenOption.setName("Green"); + this.blueOption.setName("Blue"); + } + + public void setHexControlsVisible(boolean var1) { + this.hexLabel.setVisible(var1); + this.hexField.setVisible(var1); + } + + public void setPreviewSwatchVisible(boolean var1) { + this.preview.setVisible(var1); + } + + public void setExpertControlsVisible(boolean var1) { + this.expertControls.setVisible(var1); + } + + public void setModeControlsVisible(boolean var1) { + this.hueOption.setRadioButtonVisible(var1 && this.hueOption.isVisible()); + this.saturationOption.setRadioButtonVisible(var1 && this.saturationOption.isVisible()); + this.brightnessOption.setRadioButtonVisible(var1 && this.brightnessOption.isVisible()); + this.redOption.setRadioButtonVisible(var1 && this.redOption.isVisible()); + this.greenOption.setRadioButtonVisible(var1 && this.greenOption.isVisible()); + this.blueOption.setRadioButtonVisible(var1 && this.blueOption.isVisible()); + this.putClientProperty("mode controls visible", var1); + } + + public ColorPickerMode getMode() { + return this.modeModel.getMode(); + } + + public void setMode(ColorPickerMode var1) { + if (var1 == null) { + throw new IllegalArgumentException("mode must not be null"); + } else { + this.modeModel.setMode(var1); + } + } + + public void setRGB(int var1, int var2, int var3) { + this.setColor(new Color(var1, var2, var3)); + } + + public Color getColor() { + return this.colorModel.getColor(); + } + + public void setColor(Color var1) { + Color var2 = this.colorModel.getColor(); + this.colorModel.setColor(var1); + this.firePropertyChange("selected color", var2, var1); + } + + public JPanel getExpertControls() { + return this.expertControls; + } + + public void setRGBControlsVisible(boolean var1) { + boolean var2 = this.areRadioButtonsAllowed(); + this.redOption.setVisible(var1, var2); + this.greenOption.setVisible(var1, var2); + this.blueOption.setVisible(var1, var2); + } + + private boolean areRadioButtonsAllowed() { + Boolean var1 = (Boolean)this.getClientProperty("mode controls visible"); + return var1 != null ? var1 : true; + } + + public void setHSBControlsVisible(boolean var1) { + boolean var2 = this.areRadioButtonsAllowed(); + this.hueOption.setVisible(var1, var2); + this.saturationOption.setVisible(var1, var2); + this.brightnessOption.setVisible(var1, var2); + } + + public final void setOpacityVisible(boolean var1) { + this.opacityLabel.setVisible(var1); + this.opacitySlider.setVisible(var1); + this.alphaOption.setLabelVisible(var1); + this.alphaOption.setSpinnerVisible(var1); + } + + public ColorPickerPanel getColorPanel() { + return this.colorPanel; + } + + public void setHSB(float var1, float var2, float var3) { + requireValidFloat(var1, "hue"); + requireValidFloat(var2, "saturation"); + requireValidFloat(var3, "brightness"); + this.setColor(Color.getHSBColor(var1, var2, var3)); + } + + public float[] getHSB() { + return this.colorModel.getHSB(); + } + + public int[] getRGB() { + return this.colorModel.getRGB(); + } + + public void setOpacity(int var1) { + this.setColor(new Color(this.colorModel.getRed(), this.colorModel.getGreen(), this.colorModel.getBlue(), var1)); + } + + public void addColorListener(ColorListener var1) { + this.colorModel.addColorListener(var1); + } + + public void removeColorListener(ColorListener var1) { + this.colorModel.removeColorListener(var1); + } +} diff --git a/testData/results/custom-classes/hp888/com/bric/colorpicker/ColorPickerMode.dec b/testData/results/custom-classes/hp888/com/bric/colorpicker/ColorPickerMode.dec new file mode 100644 index 0000000..7bc3b4d --- /dev/null +++ b/testData/results/custom-classes/hp888/com/bric/colorpicker/ColorPickerMode.dec @@ -0,0 +1,21 @@ +package com.bric.colorpicker; + +public enum ColorPickerMode { + HUE(360), + BRIGHTNESS(100), + SATURATION(100), + RED(255), + GREEN(255), + BLUE(255), + ALPHA(255); + + private int max; + + private ColorPickerMode(int var3) { + this.max = var3; + } + + public int getMax() { + return this.max; + } +} diff --git a/testData/results/custom-classes/hp888/lIllIIllllllIlIIIIlIllIIlIIlllIIllIIIllIIlIlIIllIl.dec b/testData/results/custom-classes/hp888/lIllIIllllllIlIIIIlIllIIlIIlllIIllIIIllIIlIlIIllIl.dec deleted file mode 100644 index d4c22375daa745cf9a7db03f2dd2d1be482eb076..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 69 zcmXRYP0GnkR!GiCEG|}H$nng{@dRQJ@C34eY={V05JZ54KuSQeAT2lL6z#JC|HG6I0f`obC~t9U1qjjB-w-*cCt zIHE+Eb&)Y(E_3Jb-2JMM;iFtCQNZ=Lt3v68F-D^k?mANMBJ4!R3F$oU{nwq8;$Rx zGXE^+iE=%EsdOv7u>pd_55mPm_ec=`=t>0XAxZAXvl#*qmya?J@Q10Dr>pw@OF(qJw`GXR&_kcw2fStd8Q!53Qh^kbR7EQQ}Fcq*AuM zdd_7v`2u%wJu!c{@QsPtZ>?JWmKx2#_JKvBUo;plIvy^1GF_dbDi4HbI=S+1OXG>0A%TEY|CGI&8s73%XUu$(g zdoc*q8~}kn^kRh5g9uhZiOR4kKpp;Y+9roC4_ETP3zz1WlK3yYJYA z$zu0h+hl%hOM>`&rX}(G;dlcY5QHUy?QKA*jI+M>(+nUGNjR^x#&Mm;n`t&3KVDvB zH}kLmT#ny=QFnj8n@k+rDd=J9V=#aK1_r;C4=?Rs8&sR#c8lAH@Q0sifX4TK%|F3t zBxF$}Yu7v=<*m3Wm9EQ-wPHz^)7>Lcqu!>s+QXwkX7Sy~>MLF-iDipNC6Da6HUFV& zal#3@rf()6@iX`j478;Pnh2Ls2tQ)^26y)R2Y0wOA+ZxA#*fSl4DOdI%~xtKB|L3t z!O;V>P|#58cn^1Q_0z*$!;P%f#EH)i!>z8V@CYuetdWPHRtKPBcNptfiq8tcL9^R! z%=`Xg2DP>a639N+FwUq@qu|Ri&KnH7`8I0dbpVeG!su7>_5aZEa>LJ0RJPPnzp7TQ zP-ls=RsP7)h~Dh)Lyfw7GOVaM!|3q1FL?S>3878LX})4TZhF_|2MwD0bSGV>>q@KC u&ui@$Fk$E