diff --git a/runelite-api/src/main/java/net/runelite/api/Client.java b/runelite-api/src/main/java/net/runelite/api/Client.java index 16523deab1..bc9bdd1b51 100644 --- a/runelite-api/src/main/java/net/runelite/api/Client.java +++ b/runelite-api/src/main/java/net/runelite/api/Client.java @@ -2327,4 +2327,6 @@ public interface Client extends GameEngine */ @Nullable ClanSettings getClanSettings(int clanId); + + void setUnlockedFps(boolean unlock); } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/fps/FpsConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/fps/FpsConfig.java index feba6256cb..adeabdaeda 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/fps/FpsConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/fps/FpsConfig.java @@ -46,7 +46,7 @@ public interface FpsConfig extends Config @Range( min = 1, - max = 50 + max = 360 ) @ConfigItem( keyName = "maxFps", @@ -72,7 +72,7 @@ public interface FpsConfig extends Config @Range( min = 1, - max = 50 + max = 360 ) @ConfigItem( keyName = "maxFpsUnfocused", diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java index 228fd4a3dd..d64e1b103f 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java @@ -78,6 +78,7 @@ import net.runelite.api.hooks.DrawCallbacks; import net.runelite.client.callback.ClientThread; import net.runelite.client.config.ConfigManager; import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.events.ConfigChanged; import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.PluginDescriptor; import net.runelite.client.plugins.PluginInstantiationException; @@ -387,7 +388,10 @@ public class GpuPlugin extends Plugin implements DrawCallbacks } this.gl = glContext.getGL().getGL4(); - gl.setSwapInterval(0); + + final boolean unlockFps = this.config.unlockFps(); + client.setUnlockedFps(unlockFps); + gl.setSwapInterval(unlockFps ? 1 : 0); if (log.isDebugEnabled()) { @@ -459,6 +463,7 @@ public class GpuPlugin extends Plugin implements DrawCallbacks { client.setGpu(false); client.setDrawCallbacks(null); + client.setUnlockedFps(false); invokeOnMainThread(() -> { @@ -529,6 +534,23 @@ public class GpuPlugin extends Plugin implements DrawCallbacks return configManager.getConfig(GpuPluginConfig.class); } + @Subscribe + public void onConfigChanged(ConfigChanged configChanged) + { + if (configChanged.getGroup().equals(GpuPluginConfig.GROUP)) + { + if (configChanged.getKey().equals("unlockFps")) + { + boolean unlockFps = Boolean.parseBoolean(configChanged.getNewValue()); + clientThread.invokeLater(() -> + { + client.setUnlockedFps(unlockFps); + invokeOnMainThread(() -> gl.setSwapInterval(unlockFps ? 1 : 0)); + }); + } + } + } + private void initProgram() throws ShaderException { String versionHeader = OSType.getOSType() == OSType.Linux ? LINUX_VERSION_HEADER : WINDOWS_VERSION_HEADER; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPluginConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPluginConfig.java index 9f276fa8e1..ff69cd80e8 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPluginConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPluginConfig.java @@ -34,9 +34,11 @@ import net.runelite.client.plugins.gpu.config.AntiAliasingMode; import net.runelite.client.plugins.gpu.config.ColorBlindMode; import net.runelite.client.plugins.gpu.config.UIScalingMode; -@ConfigGroup("gpu") +@ConfigGroup(GpuPluginConfig.GROUP) public interface GpuPluginConfig extends Config { + String GROUP = "gpu"; + @Range( max = MAX_DISTANCE ) @@ -146,4 +148,15 @@ public interface GpuPluginConfig extends Config { return false; } + + @ConfigItem( + keyName = "unlockFps", + name = "Unlock FPS", + description = "Removes the 50 FPS cap for camera movement", + position = 10 + ) + default boolean unlockFps() + { + return false; + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculator.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculator.java index 672996ebeb..3645a3228a 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculator.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculator.java @@ -34,7 +34,6 @@ import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; -import java.text.DecimalFormat; import java.text.NumberFormat; import java.util.ArrayList; import java.util.List; @@ -62,7 +61,6 @@ import net.runelite.client.ui.components.IconTextField; class SkillCalculator extends JPanel { private static final int MAX_XP = 200_000_000; - private static final DecimalFormat XP_FORMAT = new DecimalFormat("#.#"); private final UICalculatorInputArea uiInput; private final Client client; @@ -203,7 +201,7 @@ class SkillCalculator extends JPanel int actionCount = 0; int neededXP = targetXP - currentXP; - double xp = 0; + int xp = 0; for (UIActionSlot slot : combinedActionSlots) { @@ -213,7 +211,8 @@ class SkillCalculator extends JPanel if (neededXP > 0) { assert xp != 0; - actionCount = (int) Math.ceil(neededXP / xp); + neededXP *= 10; + actionCount = (neededXP - 1) / xp + 1; } combinedActionSlot.setText(formatXPActionString(xp, actionCount, "exp - ")); @@ -344,11 +343,12 @@ class SkillCalculator extends JPanel int neededXP = targetXP - currentXP; SkillAction action = slot.getAction(); final float bonus = action.isIgnoreBonus() ? 1f : xpFactor; - final double xp = Math.round(action.getXp() * bonus * 10f) / 10d; + final int xp = Math.round(action.getXp() * bonus * 10f); if (neededXP > 0) { - actionCount = (int) Math.ceil(neededXP / xp); + neededXP *= 10; + actionCount = (neededXP - 1) / xp + 1; } slot.setText("Lvl. " + action.getLevel() + " (" + formatXPActionString(xp, actionCount, "exp) - ")); @@ -360,9 +360,11 @@ class SkillCalculator extends JPanel updateCombinedAction(); } - private static String formatXPActionString(double xp, int actionCount, String expExpression) + private static String formatXPActionString(int xp, int actionCount, String expExpression) { - return XP_FORMAT.format(xp) + expExpression + NumberFormat.getIntegerInstance().format(actionCount) + (actionCount == 1 ? " action" : " actions"); + int integer = xp / 10; + int frac = xp % 10; + return (frac != 0 ? (integer + "." + frac) : integer) + expExpression + NumberFormat.getIntegerInstance().format(actionCount) + (actionCount == 1 ? " action" : " actions"); } private void updateInputFields() diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/UIActionSlot.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/UIActionSlot.java index aa2fd23937..31d6f3340d 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/UIActionSlot.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/UIActionSlot.java @@ -85,7 +85,7 @@ class UIActionSlot extends JPanel @Getter(AccessLevel.PACKAGE) @Setter(AccessLevel.PACKAGE) - private double value; + private int value; UIActionSlot(SkillAction action, ClientThread clientThread, ItemManager itemManager, JLabel uiIcon) { diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/FletchingAction.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/FletchingAction.java index c869ed06a2..011441f539 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/FletchingAction.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/FletchingAction.java @@ -35,6 +35,7 @@ public enum FletchingAction implements ItemSkillAction ARROW_SHAFT(ItemID.ARROW_SHAFT, 1, 0.33f), HEADLESS_ARROW(ItemID.HEADLESS_ARROW, 1, 1), BRONZE_ARROW(ItemID.BRONZE_ARROW, 1, 1.3f), + BRONZE_JAVELIN(ItemID.BRONZE_JAVELIN, 3, 1), OGRE_ARROW(ItemID.OGRE_ARROW, 5, 1), SHORTBOW_U(ItemID.SHORTBOW_U, 5, 5), SHORTBOW(ItemID.SHORTBOW, 5, 5), @@ -47,6 +48,7 @@ public enum FletchingAction implements ItemSkillAction LONGBOW_U(ItemID.LONGBOW_U, 10, 10), OPAL_BOLTS(ItemID.OPAL_BOLTS, 11, 1.6f), IRON_ARROW(ItemID.IRON_ARROW, 15, 2.5f), + IRON_JAVELIN(ItemID.IRON_JAVELIN, 17, 2), OAK_SHORTBOW_U(ItemID.OAK_SHORTBOW_U, 20, 16.5f), OAK_SHORTBOW(ItemID.OAK_SHORTBOW, 20, 16.5f), IRON_DART(ItemID.IRON_DART, 22, 3.8f), @@ -57,6 +59,7 @@ public enum FletchingAction implements ItemSkillAction OAK_LONGBOW(ItemID.OAK_LONGBOW, 25, 25), OAK_SHIELD(ItemID.OAK_SHIELD, 27, 50), STEEL_ARROW(ItemID.STEEL_ARROW, 30, 5), + STEEL_JAVELIN(ItemID.STEEL_JAVELIN, 32, 5), KEBBIT_BOLTS(ItemID.KEBBIT_BOLTS, 32, 1), WILLOW_SHORTBOW_U(ItemID.WILLOW_SHORTBOW_U, 35, 33.3f), WILLOW_SHORTBOW(ItemID.WILLOW_SHORTBOW, 35, 33.3f), @@ -77,6 +80,7 @@ public enum FletchingAction implements ItemSkillAction TEAK_STOCK(ItemID.TEAK_STOCK, 46, 27), STEEL_CROSSBOW_U(ItemID.STEEL_CROSSBOW_U, 46, 54), STEEL_CROSSBOW(ItemID.STEEL_CROSSBOW, 46, 27), + MITHRIL_JAVELIN(ItemID.MITHRIL_JAVELIN, 47, 8), MAPLE_SHORTBOW_U(ItemID.MAPLE_SHORTBOW_U, 50, 50), MAPLE_SHORTBOW(ItemID.MAPLE_SHORTBOW, 50, 50), BARBED_BOLTS(ItemID.BARBED_BOLTS, 51, 9.5f), @@ -98,6 +102,7 @@ public enum FletchingAction implements ItemSkillAction MAHOGANY_STOCK(ItemID.MAHOGANY_STOCK, 61, 41), ADAMANT_CROSSBOW_U(ItemID.ADAMANT_CROSSBOW_U, 61, 82), ADAMANT_CROSSBOW(ItemID.ADAMANT_CROSSBOW, 61, 41), + ADAMANT_JAVELIN(ItemID.ADAMANT_JAVELIN, 62, 10), RUBY_BOLTS(ItemID.RUBY_BOLTS, 63, 6.3f), DIAMOND_BOLTS(ItemID.DIAMOND_BOLTS, 65, 7), YEW_SHORTBOW(ItemID.YEW_SHORTBOW, 65, 67.5f), @@ -114,6 +119,7 @@ public enum FletchingAction implements ItemSkillAction ONYX_BOLTS(ItemID.ONYX_BOLTS, 73, 9.4f), RUNE_ARROW(ItemID.RUNE_ARROW, 75, 12.5f), AMETHYST_BROAD_BOLTS(ItemID.AMETHYST_BROAD_BOLTS, 76, 10.6f), + RUNE_JAVELIN(ItemID.RUNE_JAVELIN, 77, 12.4f), MAGIC_STOCK(ItemID.MAGIC_STOCK, 78, 70), DRAGON_CROSSBOW_U(ItemID.DRAGON_CROSSBOW_U, 78, 135), DRAGON_CROSSBOW(ItemID.DRAGON_CROSSBOW, 78, 70), @@ -128,6 +134,7 @@ public enum FletchingAction implements ItemSkillAction MAGIC_SHIELD(ItemID.MAGIC_SHIELD, 87, 183), AMETHYST_DART(ItemID.AMETHYST_DART, 90, 21), DRAGON_ARROW(ItemID.DRAGON_ARROW, 90, 15), + DRAGON_JAVELIN(ItemID.DRAGON_JAVELIN, 92, 15), REDWOOD_SHIELD(ItemID.REDWOOD_SHIELD, 92, 216), DRAGON_DART(ItemID.DRAGON_DART, 95, 25), ; diff --git a/runelite-client/src/main/java/net/runelite/client/rs/ClientLoader.java b/runelite-client/src/main/java/net/runelite/client/rs/ClientLoader.java index 6afd761a68..aca456df93 100644 --- a/runelite-client/src/main/java/net/runelite/client/rs/ClientLoader.java +++ b/runelite-client/src/main/java/net/runelite/client/rs/ClientLoader.java @@ -78,7 +78,7 @@ import okhttp3.Response; public class ClientLoader implements Supplier { private static final String INJECTED_CLIENT_NAME = "/injected-client.oprs"; - private static final int NUM_ATTEMPTS = 0; + private static final int NUM_ATTEMPTS = 6; private static File LOCK_FILE = new File(RuneLite.CACHE_DIR, "cache.lock"); private static File VANILLA_CACHE = new File(RuneLite.CACHE_DIR, "vanilla.cache"); private static File PATCHED_CACHE = new File(RuneLite.CACHE_DIR, "patched.cache"); @@ -266,7 +266,10 @@ public class ClientLoader implements Supplier private void updateVanilla(RSConfig config) throws IOException, VerificationException { - Certificate[] jagexCertificateChain = getJagexCertificateChain(); + Certificate[][] jagexCertificateChains = { + loadCertificateChain("jagex.crt"), + loadCertificateChain("jagex2021.crt") + }; // Get the mtime of the first thing in the vanilla cache // we check this against what the server gives us to let us skip downloading and patching the whole thing @@ -283,7 +286,7 @@ public class ClientLoader implements Supplier JarEntry je = vanillaCacheTest.getNextJarEntry(); if (je != null) { - verifyJarEntry(je, jagexCertificateChain); + verifyJarEntry(je, jagexCertificateChains); vanillaCacheMTime = je.getLastModifiedTime().toMillis(); } else @@ -362,7 +365,7 @@ public class ClientLoader implements Supplier } networkJIS.skip(Long.MAX_VALUE); - verifyJarEntry(je, jagexCertificateChain); + verifyJarEntry(je, jagexCertificateChains); long vanillaClientMTime = je.getLastModifiedTime().toMillis(); if (!vanillaCacheIsInvalid && vanillaClientMTime != vanillaCacheMTime) { @@ -379,7 +382,7 @@ public class ClientLoader implements Supplier { // as with the request stream, its important to not early close vanilla too JarInputStream vanillaCacheTest = new JarInputStream(Channels.newInputStream(vanilla)); - verifyWholeJar(vanillaCacheTest, jagexCertificateChain); + verifyWholeJar(vanillaCacheTest, jagexCertificateChains); } catch (Exception e) { @@ -395,7 +398,7 @@ public class ClientLoader implements Supplier OutputStream out = Channels.newOutputStream(vanilla); out.write(preRead.toByteArray()); copyStream.setOut(out); - verifyWholeJar(networkJIS, jagexCertificateChain); + verifyWholeJar(networkJIS, jagexCertificateChains); copyStream.skip(Long.MAX_VALUE); // write the trailer to the file too out.flush(); vanilla.truncate(vanilla.position()); @@ -612,9 +615,9 @@ public class ClientLoader implements Supplier return rs; } - private static Certificate[] getJagexCertificateChain() + private static Certificate[] loadCertificateChain(String name) { - try (InputStream in = ClientLoader.class.getResourceAsStream("jagex.crt")) + try (InputStream in = ClientLoader.class.getResourceAsStream(name)) { CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); Collection certificates = certificateFactory.generateCertificates(in); @@ -626,28 +629,33 @@ public class ClientLoader implements Supplier } } - private void verifyJarEntry(JarEntry je, Certificate[] certs) throws VerificationException + private void verifyJarEntry(JarEntry je, Certificate[][] chains) throws VerificationException { - switch (je.getName()) + if (je.getName().equals("META-INF/JAGEXLTD.SF") || je.getName().equals("META-INF/JAGEXLTD.RSA")) { - case "META-INF/JAGEXLTD.SF": - case "META-INF/JAGEXLTD.RSA": - // You can't sign the signing files - return; - default: - if (!Arrays.equals(je.getCertificates(), certs)) - { - throw new VerificationException("Unable to verify jar entry: " + je.getName()); - } + // You can't sign the signing files + return; } + + // Jar entry must match one of the trusted certificate chains + Certificate[] entryCertificates = je.getCertificates(); + for (Certificate[] chain : chains) + { + if (Arrays.equals(entryCertificates, chain)) + { + return; + } + } + + throw new VerificationException("Unable to verify jar entry: " + je.getName()); } - private void verifyWholeJar(JarInputStream jis, Certificate[] certs) throws IOException, VerificationException + private void verifyWholeJar(JarInputStream jis, Certificate[][] chains) throws IOException, VerificationException { for (JarEntry je; (je = jis.getNextJarEntry()) != null; ) { jis.skip(Long.MAX_VALUE); - verifyJarEntry(je, certs); + verifyJarEntry(je, chains); } } } diff --git a/runelite-client/src/main/resources/net/runelite/client/rs/jagex2021.crt b/runelite-client/src/main/resources/net/runelite/client/rs/jagex2021.crt new file mode 100644 index 0000000000..39a381c29e --- /dev/null +++ b/runelite-client/src/main/resources/net/runelite/client/rs/jagex2021.crt @@ -0,0 +1,83 @@ +-----BEGIN CERTIFICATE----- +MIIFXjCCBEagAwIBAgIQe8nov0sYXAw/4Qpi35irpzANBgkqhkiG9w0BAQsFADBM +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhhd3RlLCBJbmMuMSYwJAYDVQQDEx10 +aGF3dGUgU0hBMjU2IENvZGUgU2lnbmluZyBDQTAeFw0yMTEwMjEwMDAwMDBaFw0y +MjEwMTkyMzU5NTlaMEkxCzAJBgNVBAYTAkdCMRIwEAYDVQQHEwlDYW1icmlkZ2Ux +EjAQBgNVBAoTCUphZ2V4IEx0ZDESMBAGA1UEAxMJSmFnZXggTHRkMIICIjANBgkq +hkiG9w0BAQEFAAOCAg8AMIICCgKCAgEArDT2DqhgDbCSihEH67vHsOokbTmLeuco +z8ApWGKgyZGdO73l0YmXzco/N681xCP8SFr1chfwc9H+mS9QB/3Tp51wqLjb7U9A +IYmQVcnJNw3glZrRzBlZJCKSbcMFUQ++0+WLzKZKY/ZW8xGsu6GsUdxLadOt90pr +Ak4SrgM02PU0RfmqyEs037ezuzyR0dMYOdTHM01l1h8M2GFD9IWrqwazDcdBUpKX +KSW1nFOmSaF7TtQTq6dIHBlXE5Y2Zob/XYCYotP/3yI3XL3QEWsjF/wkTRJr5WCC +MGgWRezNf5WaE+S431+cs70FEufD32iKsOjHhbddX9qFfHXgLaAfoozNiLCCRdS4 +gaRMD6USF6T0MbWKpe43KtBukYXHZ37bW9etwEDTo23J0TBiFPHZPpKhaEo7BcZU +eS1vTH41Bf0Siv3RkP/r5b+mkLgiqINUr+GVrcGnbpDHsvCs5UBX9kLm4EIDwB5m +PlgJV85Ou5cFYvxZZyRW1IrKHXsKIWsheRTFBnij6sJr/PKZcTWBEeSxXpk6nGnE +mNRrNV+GEVwHBltC2ReCYcu2khXVcPF9qnfCfzEkHRUNFm8QkKwbLpeyqG4923Pa ++YcPK0wEJHm5t3Mv+1u8kL6SOWm37h5Z9HRLsd7N4JFXHglqVgy3BPm77oKG6XXj +WrSquVvZG1ECAwEAAaOCAT0wggE5MB0GA1UdDgQWBBQUS46z2oXY4BEoFq6JU6kM +wCHD6DAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwMwDAYDVR0T +AQH/BAIwADA+BgNVHSAENzA1MDMGBmeBDAEEATApMCcGCCsGAQUFBwIBFhtodHRw +Oi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwKwYDVR0fBCQwIjAgoB6gHIYaaHR0cDov +L3RsLnN5bWNiLmNvbS90bC5jcmwwVwYIKwYBBQUHAQEESzBJMB8GCCsGAQUFBzAB +hhNodHRwOi8vdGwuc3ltY2QuY29tMCYGCCsGAQUFBzAChhpodHRwOi8vdGwuc3lt +Y2IuY29tL3RsLmNydDAfBgNVHSMEGDAWgBRXhptUuL6mKYrk9sLiExiJhc3ctzAN +BgkqhkiG9w0BAQsFAAOCAQEAlAYVi6THkvr28AN8ExBmTYJKkZV5bDEhc55V2Ncf +a0J/v4pzFEnZoPKQBoRcxIZtY/c1Pr7NorGt3bkmBA8tc4Ni59US9OeGE9D9XYw3 +WTOD0Cl40u+bV5dTim2fU2iQy18CtZzJsn+oXa4KHcCPl4+zZ4OLX2kPyxcSBce4 +9vzXbBg0CMj1bh3HVrB93r3pLFCJtYJutPKeZjQfapst6mQddGqIY4ghmOEhH/Sf +qEno2Q0WYd4KHuynGfjdcmcNkntdkBvzQ5yaL55zjpQIXhg0dnaQxF+NuBwX6mXz +R0sshmSJfKy9hr0IYCO422uSwB1cDZ3IZnk/nR9JpVRPRQ== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEmTCCA4GgAwIBAgIQcaC3NpXdsa/COyuaGO5UyzANBgkqhkiG9w0BAQsFADCB +qTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf +Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw +MDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNV +BAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMTMxMjEwMDAwMDAwWhcNMjMx +MjA5MjM1OTU5WjBMMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhhd3RlLCBJbmMu +MSYwJAYDVQQDEx10aGF3dGUgU0hBMjU2IENvZGUgU2lnbmluZyBDQTCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBAJtVAkwXBenQZsP8KK3TwP7v4Ol+1B72 +qhuRRv31Fu2YB1P6uocbfZ4fASerudJnyrcQJVP0476bkLjtI1xC72QlWOWIIhq+ +9ceu9b6KsRERkxoiqXRpwXS2aIengzD5ZPGx4zg+9NbB/BL+c1cXNVeK3VCNA/hm +zcp2gxPI1w5xHeRjyboX+NG55IjSLCjIISANQbcL4i/CgOaIe1Nsw0RjgX9oR4wr +Ks9b9IxJYbpphf1rAHgFJmkTMIA4TvFaVcnFUNaqOIlHQ1z+TXOlScWTaf53lpqv +84wOV7oz2Q7GQtMDd8S7Oa2R+fP3llw6ZKbtJ1fB6EDzU/K+KTT+X/kCAwEAAaOC +ARcwggETMC8GCCsGAQUFBwEBBCMwITAfBggrBgEFBQcwAYYTaHR0cDovL3QyLnN5 +bWNiLmNvbTASBgNVHRMBAf8ECDAGAQH/AgEAMDIGA1UdHwQrMCkwJ6AloCOGIWh0 +dHA6Ly90MS5zeW1jYi5jb20vVGhhd3RlUENBLmNybDAdBgNVHSUEFjAUBggrBgEF +BQcDAgYIKwYBBQUHAwMwDgYDVR0PAQH/BAQDAgEGMCkGA1UdEQQiMCCkHjAcMRow +GAYDVQQDExFTeW1hbnRlY1BLSS0xLTU2ODAdBgNVHQ4EFgQUV4abVLi+pimK5PbC +4hMYiYXN3LcwHwYDVR0jBBgwFoAUe1tFz6/Oy3r9MZIaarbzRutXSFAwDQYJKoZI +hvcNAQELBQADggEBACQ79degNhPHQ/7wCYdo0ZgxbhLkPx4flntrTB6HnovFbKOx +DHtQktWBnLGPLCm37vmRBbmOQfEs9tBZLZjgueqAAUdAlbg9nQO9ebs1tq2cTCf2 +Z0UQycW8h05Ve9KHu93cMO/G1GzMmTVtHOBg081ojylZS4mWCEbJjvx1T8XcCcxO +J4tEzQe8rATgtTOlh5/03XMMkeoSgW/jdfAetZNsRBfVPpfJvQcsVncfhd1G6L/e +LIGUo/flt6fBN591ylV3TV42KcqF2EVBcld1wHlb+jQQBm1kIEK3OsgfHUZkAl/G +R77wxDooVNr2Hk+aohlDpG9J+PxeQiAohItHIG4= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEIDCCAwigAwIBAgIQNE7VVyDV7exJ9C/ON9srbTANBgkqhkiG9w0BAQUFADCB +qTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf +Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw +MDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNV +BAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMDYxMTE3MDAwMDAwWhcNMzYw +NzE2MjM1OTU5WjCBqTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5j +LjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYG +A1UECxMvKGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl +IG9ubHkxHzAdBgNVBAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCsoPD7gFnUnMekz52hWXMJEEUMDSxuaPFs +W0hoSVk3/AszGcJ3f8wQLZU0HObrTQmnHNK4yZc2AreJ1CRfBsDMRJSUjQJib+ta +3RGNKJpchJAQeg29dGYvajig4tVUROsdB58Hum/u6f1OCyn1PoSgAfGcq/gcfomk +6KHYcWUNo1F77rzSImANuVud37r8UVsLr5iy6S7pBOhih94ryNdOwUxkHt3Ph1i6 +Sk/KaAcdHJ1KxtUvkcx8cXIcxcBn6zL9yZJclNqFwJu/U30rCfSMnZEfl2pSy94J +NqR32HuHUETVPm4pafs5SSYeCaWAe0At6+gnhcn+Yf1+5nyXHdWdAgMBAAGjQjBA +MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBR7W0XP +r87Lev0xkhpqtvNG61dIUDANBgkqhkiG9w0BAQUFAAOCAQEAeRHAS7ORtvzw6WfU +DW5FvlXok9LOAz/t2iWwHVfLHjp2oEzsUHboZHIMpKnxuIvW1oeEuzLlQRHAd9mz +YJ3rG9XRbkREqaYB7FViHXe4XI5ISXycO1cRrK1zN44veFyQaEfZYGDm/Ac9IiAX +xPcW6cTYcvnIc3zfFi8VqT79aie2oetaupgf1eNNZAqdE8hhuvU5HIe6uL17In/2 +/qxAeeWsEG89jxt5dovEN7MhGITlNgDrYyCZuen+MwS7QcjBAvlEYyCegc5C09Y/ +LHbTY5xZ3Y+m4Q6gLkH3LpVHz7z9M/P2C2F+fpErgUfCJzDupxBdN49cOSvkBPB7 +jVaMaA== +-----END CERTIFICATE----- \ No newline at end of file