├── .editorconfig
├── .gitattributes
├── .github
└── ISSUE_TEMPLATE
│ └── crashing---not-working.md
├── .gitignore
├── .mvn
└── wrapper
│ ├── maven-wrapper.jar
│ └── maven-wrapper.properties
├── CHANGELOG.md
├── LICENSE
├── MTS.cmd
├── MTS.sh
├── MTS_8u51.cmd
├── README.md
├── _run.bat
├── mvnw
├── mvnw.cmd
├── pom.xml
└── src
└── main
├── java
├── com
│ ├── evacipated
│ │ └── cardcrawl
│ │ │ └── modthespire
│ │ │ ├── ByteArrayMapClassPath.java
│ │ │ ├── CyclicDependencyException.java
│ │ │ ├── DownloadAndRestarter.java
│ │ │ ├── DuplicateModIDException.java
│ │ │ ├── EnumBusterReflect.java
│ │ │ ├── GameBetaFinder.java
│ │ │ ├── GameVersionFinder.java
│ │ │ ├── GithubUpdateChecker.java
│ │ │ ├── GraphTS.java
│ │ │ ├── Loader.java
│ │ │ ├── MTSClassLoader.java
│ │ │ ├── MTSClassPool.java
│ │ │ ├── MissingDependencyException.java
│ │ │ ├── MissingModIDException.java
│ │ │ ├── ModInfo.java
│ │ │ ├── ModList.java
│ │ │ ├── ModUpdate.java
│ │ │ ├── OutJar.java
│ │ │ ├── PackageJar.java
│ │ │ ├── Patcher.java
│ │ │ ├── ReflectionHelper.java
│ │ │ ├── UpdateChecker.java
│ │ │ ├── finders
│ │ │ ├── InOrderFinder.java
│ │ │ ├── InOrderMultiFinder.java
│ │ │ └── MatchFinderExprEditor.java
│ │ │ ├── lib
│ │ │ ├── ByRef.java
│ │ │ ├── ConfigUtils.java
│ │ │ ├── LineFinder.java
│ │ │ ├── Matcher.java
│ │ │ ├── SpireConfig.java
│ │ │ ├── SpireEnum.java
│ │ │ ├── SpireField.java
│ │ │ ├── SpireInitializer.java
│ │ │ ├── SpireInsertLocator.java
│ │ │ ├── SpireInsertPatch.java
│ │ │ ├── SpireInstrumentPatch.java
│ │ │ ├── SpireOverride.java
│ │ │ ├── SpirePatch.java
│ │ │ ├── SpirePatch2.java
│ │ │ ├── SpirePatches.java
│ │ │ ├── SpirePatches2.java
│ │ │ ├── SpirePostfixPatch.java
│ │ │ ├── SpirePrefixPatch.java
│ │ │ ├── SpireRawPatch.java
│ │ │ ├── SpireReturn.java
│ │ │ ├── SpireSideload.java
│ │ │ ├── SpireSuper.java
│ │ │ └── StaticSpireField.java
│ │ │ ├── patcher
│ │ │ ├── ByRefParameterNotArrayException.java
│ │ │ ├── ClassPatchInfo.java
│ │ │ ├── Expectation.java
│ │ │ ├── InsertPatchInfo.java
│ │ │ ├── InstrumentPatchInfo.java
│ │ │ ├── LocatorInfo.java
│ │ │ ├── MissingParamTypesException.java
│ │ │ ├── NonStaticPatchMethodException.java
│ │ │ ├── ParamInfo.java
│ │ │ ├── ParamInfo2.java
│ │ │ ├── ParameterPatchInfo.java
│ │ │ ├── PatchInfo.java
│ │ │ ├── PatchInfoComparator.java
│ │ │ ├── PatchingException.java
│ │ │ ├── PostfixPatchInfo.java
│ │ │ ├── PrefixPatchInfo.java
│ │ │ ├── RawPatchInfo.java
│ │ │ ├── ReplacePatchInfo.java
│ │ │ └── javassist
│ │ │ │ ├── MyCodeConverter.java
│ │ │ │ └── convert
│ │ │ │ └── TransformSpecialCallVirtual.java
│ │ │ ├── patches
│ │ │ ├── AllowWatcherUnlock.java
│ │ │ ├── AlwaysEnableCustomMode.java
│ │ │ ├── CatchCrash.java
│ │ │ ├── ChangeWindowTitle.java
│ │ │ ├── CreditsModList.java
│ │ │ ├── HandleCrash.java
│ │ │ ├── HeapSize.java
│ │ │ ├── IsModded.java
│ │ │ ├── MainMenuModList.java
│ │ │ ├── SkipIntro.java
│ │ │ ├── TopPanelModList.java
│ │ │ ├── lwjgl2
│ │ │ │ ├── CatchCrash.java
│ │ │ │ └── Lwjgl2DisableGdxForceExit.java
│ │ │ ├── lwjgl3
│ │ │ │ ├── CatchCrash.java
│ │ │ │ ├── CloseMTSWindow.java
│ │ │ │ ├── FixController.java
│ │ │ │ ├── FramerateLimiter.java
│ │ │ │ ├── Lwjgl2To3.java
│ │ │ │ ├── SaveWindowPosition.java
│ │ │ │ └── Sync.java
│ │ │ └── modsscreen
│ │ │ │ ├── BaseMod
│ │ │ │ ├── DisableBaseModBadges.java
│ │ │ │ └── ModBadgeOnClick.java
│ │ │ │ ├── MainMenuItem.java
│ │ │ │ ├── ModMenuButton.java
│ │ │ │ ├── ModsScreen.java
│ │ │ │ ├── ModsScreenUpdateRender.java
│ │ │ │ └── SaveBaseModBadges.java
│ │ │ ├── steam
│ │ │ ├── SteamSearch.java
│ │ │ └── SteamWorkshop.java
│ │ │ └── ui
│ │ │ ├── JModPanelCheckBoxList.java
│ │ │ ├── MessageConsole.java
│ │ │ ├── ModPanel.java
│ │ │ ├── ModSelectWindow.java
│ │ │ ├── TextFieldWithPlaceholder.java
│ │ │ └── UpdateWindow.java
│ └── megacrit
│ │ └── cardcrawl
│ │ └── desktop
│ │ └── DesktopLauncher.java
├── javassist
│ └── bytecode
│ │ └── annotation
│ │ └── MemberValue.java
└── org
│ └── clapper
│ └── util
│ ├── classutil
│ ├── AbstractClassFilter.java
│ ├── AndClassFilter.java
│ ├── ClassFilter.java
│ ├── ClassFinder.java
│ ├── ClassInfo.java
│ ├── ClassInfoClassVisitor.java
│ ├── ClassLoaderBuilder.java
│ ├── ClassModifiersClassFilter.java
│ ├── ClassUtil.java
│ ├── ClassUtilException.java
│ ├── FieldInfo.java
│ ├── InterfaceOnlyClassFilter.java
│ ├── MethodInfo.java
│ ├── NotClassFilter.java
│ ├── OrClassFilter.java
│ ├── RegexClassFilter.java
│ └── SubclassClassFilter.java
│ ├── cmdline
│ ├── CommandLineException.java
│ ├── CommandLineUsageException.java
│ ├── CommandLineUserException.java
│ ├── CommandLineUtility.java
│ ├── OptionComparator.java
│ ├── OptionInfo.java
│ ├── Package.java
│ ├── ParameterHandler.java
│ ├── ParameterParser.java
│ └── UsageInfo.java
│ ├── config
│ ├── Configuration.java
│ ├── ConfigurationException.java
│ ├── EnvSection.java
│ ├── NoSuchSectionException.java
│ ├── NoSuchVariableException.java
│ ├── Package.java
│ ├── ProgramSection.java
│ ├── Section.java
│ ├── SectionExistsException.java
│ ├── SystemSection.java
│ ├── ValueSegment.java
│ ├── Variable.java
│ └── package.html
│ ├── html
│ └── HTMLUtil.java
│ ├── io
│ ├── AndFileFilter.java
│ ├── AndFilenameFilter.java
│ ├── CombinationFileFilter.java
│ ├── CombinationFilenameFilter.java
│ ├── CombinationFilterMode.java
│ ├── DirectoryFilter.java
│ ├── FileFilterMatchType.java
│ ├── FileNameComparator.java
│ ├── FileOnlyFilter.java
│ ├── FileUtil.java
│ ├── IOExceptionExt.java
│ ├── JustifyStyle.java
│ ├── JustifyTextWriter.java
│ ├── MultipleRegexFilenameFilter.java
│ ├── NotFileFilter.java
│ ├── NotFilenameFilter.java
│ ├── OrFileFilter.java
│ ├── OrFilenameFilter.java
│ ├── Package.java
│ ├── RecursiveFileFinder.java
│ ├── RegexFileFilter.java
│ ├── RegexFilenameFilter.java
│ ├── RollingFileWriter.java
│ ├── WordWrapWriter.java
│ ├── XMLWriter.java
│ ├── Zipper.java
│ └── package.html
│ ├── logging
│ ├── JavaUtilLoggingTextFormatter.java
│ ├── LogLevel.java
│ ├── Logger.java
│ └── package.html
│ ├── misc
│ ├── ArrayIterator.java
│ ├── BuildInfo.java
│ ├── BundleUtil.java
│ ├── EnumerationIterator.java
│ ├── FileHashMap.java
│ ├── FileHashMapEntry.java
│ ├── LRUMap.java
│ ├── MIMETypeUtil.java
│ ├── MultiIterator.java
│ ├── MultiValueMap.java
│ ├── NestedException.java
│ ├── ObjectExistsException.java
│ ├── ObjectLockSemaphore.java
│ ├── ObjectRemovalEvent.java
│ ├── ObjectRemovalListener.java
│ ├── OrderedHashMap.java
│ ├── Package.java
│ ├── PropertiesMap.java
│ ├── Semaphore.java
│ ├── SemaphoreException.java
│ ├── SparseArrayList.java
│ ├── Version.java
│ ├── VersionBase.java
│ ├── VersionMismatchException.java
│ ├── XDate.java
│ ├── XResourceBundle.java
│ └── package.html
│ ├── regex
│ ├── Package.java
│ ├── RegexException.java
│ └── RegexUtil.java
│ └── text
│ ├── AbstractVariableSubstituter.java
│ ├── Duration.java
│ ├── MapVariableDereferencer.java
│ ├── MultipleMapVariableDereferencer.java
│ ├── Package.java
│ ├── TextUtil.java
│ ├── UndefinedVariableException.java
│ ├── Unicode.java
│ ├── UnixShellVariableSubstituter.java
│ ├── VariableDereferencer.java
│ ├── VariableNameChecker.java
│ ├── VariableSubstituter.java
│ ├── VariableSubstitutionException.java
│ ├── VariableSyntaxException.java
│ ├── WindowsCmdVariableSubstituter.java
│ ├── XStringBufBase.java
│ ├── XStringBuffer.java
│ ├── XStringBuilder.java
│ └── package.html
└── resources
├── META-INF
├── licences
│ ├── javassist
│ │ └── License.html
│ └── scannotation
│ │ └── LICENSE
└── version.prop
├── ModTheSpire.json
├── assets
├── ajax-loader.gif
├── download.gif
├── error.gif
├── good.gif
├── icon.png
├── update.gif
├── warning.gif
└── workshop.gif
└── steam_appid.txt
/.editorconfig:
--------------------------------------------------------------------------------
1 | [*]
2 | charset=utf-8
3 | end_of_line=lf
4 | insert_final_newline=true
5 | indent_style=space
6 | indent_size=4
7 |
8 | [{.babelrc,.stylelintrc,.eslintrc,jest.config,*.bowerrc,*.jsb3,*.jsb2,*.json}]
9 | indent_style=space
10 | indent_size=2
11 |
12 | [{*.applejs,*.js}]
13 | indent_style=space
14 | indent_size=2
15 |
16 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
2 | *.cmd eol=crlf
3 | *.sh eol=lf
4 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/crashing---not-working.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Crashing / Not Working
3 | about: Is ModTheSpire crashing for you?
4 |
5 | ---
6 |
7 | **Before submitting, have you read the [Troubleshooting guide](https://github.com/kiooeht/ModTheSpire/wiki/Troubleshooting)?**
8 | Yes/No.
9 |
10 | **Crash Log**
11 | ```
12 | Paste the ENTIRE log here. Including the very top of the log and the full error.
13 | ```
14 |
15 |
16 | **Additional context**
17 | Add any other context about the problem here.
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled class file
2 | *.class
3 |
4 | # Log file
5 | *.log
6 |
7 | # BlueJ files
8 | *.ctxt
9 |
10 | # Mobile Tools for Java (J2ME)
11 | .mtj.tmp/
12 |
13 | # Package Files #
14 | *.jar
15 | *.war
16 | *.ear
17 | *.zip
18 | *.tar.gz
19 | *.rar
20 |
21 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
22 | hs_err_pid*
23 |
24 | # IntelliJ
25 | .idea/
26 | out/
27 | ModTheSpire.iml
28 |
29 | # Eclipse
30 | .classpath
31 | .project
32 | .settings/
33 | .factorypath
34 |
35 | # maven build target
36 | target/
37 |
38 | # Custom
39 | _archive/
40 | backup/
41 | bin/
42 | crashlogs/
43 | decompiled/
44 | mods/
45 | preferences/
46 | saves/
47 | tools/
48 | _build.bat
49 | _decompile.bat
50 | _runJ9.bat
51 | *.displayconfig
52 |
53 | # maven other
54 | dependency-reduced-pom.xml
55 |
56 | # maven wrapper
57 | !/.mvn/wrapper/maven-wrapper.jar
58 |
--------------------------------------------------------------------------------
/.mvn/wrapper/maven-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kiooeht/ModTheSpire/c6201995b8f5d97620cdb00f28a724a95b7742a6/.mvn/wrapper/maven-wrapper.jar
--------------------------------------------------------------------------------
/.mvn/wrapper/maven-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.2/apache-maven-3.5.2-bin.zip
2 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Anthony Moore
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/MTS.cmd:
--------------------------------------------------------------------------------
1 | start .\jre\bin\javaw.exe -jar .\ModTheSpire.jar
--------------------------------------------------------------------------------
/MTS.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | ./jre/bin/java -jar ModTheSpire.jar
--------------------------------------------------------------------------------
/MTS_8u51.cmd:
--------------------------------------------------------------------------------
1 | start .\jre1.8.0_51\bin\javaw.exe -jar .\ModTheSpire.jar
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ModTheSpire #
2 | ModTheSpire is a tool to load external mods for Slay the Spire without modifying the base game files.
3 |
4 | ## Usage ##
5 | ### Installation ###
6 | 1. Download the latest [Release](https://github.com/kiooeht/ModTheSpire/releases).
7 | 2. Copy `ModTheSpire.jar` to your Slay the Spire install directory.
8 | * For Windows, copy `MTS.cmd` to your Slay the Spire install directory.
9 | * For Linux, copy `MTS.sh` to your Slay the Spire install directory and make it executable.
10 | 3. Create a `mods` directory. Place mod JAR files into the `mods` directory.
11 |
12 | ### Running Mods ###
13 | 1. Run ModTheSpire.
14 | * For Windows, run `MTS.cmd`.
15 | * For Linux, run `MTS.sh`.
16 | * Or run `ModTheSpire.jar` with Java 8.
17 | 2. Select the mod(s) you want to use.
18 | 3. Press 'Play'.
19 |
20 | ---
21 |
22 | ## For Modders ##
23 | ### Requirements ###
24 | * JDK 8
25 | * Maven
26 |
27 | ### General ###
28 | * ModTheSpire automatically sets the Settings.isModded flag to true, so there is no need to do that yourself.
29 | * [Wiki](https://github.com/kiooeht/ModTheSpire/wiki/SpirePatch)
30 |
31 | ### Building ###
32 | 1. Run `mvnw package`
33 |
34 | ---
35 |
36 | ## Changelog ##
37 | See [CHANGELOG](CHANGELOG.md)
38 |
39 | ## Contributors ##
40 | * kiooeht - Original author
41 | * t-larson - Multi-loading, mod initialization, some UI work
42 | * test447 - Some launcher UI work, Locator
43 | * reckter - Maven setup
44 | * FlipskiZ - Mod initialization
45 | * pk27602017 - UTF-8 support in ModInfo
46 |
--------------------------------------------------------------------------------
/_run.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | java -jar "ModTheSpire.jar"
3 | pause
4 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/ByteArrayMapClassPath.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire;
2 |
3 | import javassist.CannotCompileException;
4 | import javassist.ClassPath;
5 | import javassist.CtClass;
6 | import javassist.NotFoundException;
7 |
8 | import java.io.ByteArrayInputStream;
9 | import java.io.IOException;
10 | import java.io.InputStream;
11 | import java.net.URL;
12 | import java.util.HashMap;
13 | import java.util.Map;
14 |
15 | public class ByteArrayMapClassPath implements ClassPath
16 | {
17 | protected Map classes = new HashMap<>();
18 |
19 | public ByteArrayMapClassPath() {}
20 |
21 | public void addClass(CtClass ctClass) throws CannotCompileException
22 | {
23 | try {
24 | URL url = null;
25 | try {
26 | url = ctClass.getURL();
27 | } catch (NotFoundException ignored) {}
28 | classes.put(ctClass.getName(), new Info(url, ctClass.toBytecode()));
29 | } catch (IOException e) {
30 | throw new CannotCompileException(e);
31 | }
32 | }
33 |
34 | @Override
35 | public void close() {}
36 |
37 | @Override
38 | public InputStream openClassfile(String classname)
39 | {
40 | Info classInfo = classes.get(classname);
41 | if (classInfo != null) {
42 | return new ByteArrayInputStream(classInfo.classfile);
43 | }
44 | return null;
45 | }
46 |
47 | @Override
48 | public URL find(String classname)
49 | {
50 | if (classes.containsKey(classname)) {
51 | return classes.get(classname).url;
52 | }
53 |
54 | return null;
55 | }
56 |
57 | public void printDebugInfo()
58 | {
59 | int bytes = 0;
60 | for (Info info : classes.values()) {
61 | bytes += info.classfile.length;
62 | }
63 | bytes /= 1024;
64 |
65 | System.out.printf("%d modified classes (%dKB)%n", classes.size(), bytes);
66 | }
67 |
68 | private static class Info
69 | {
70 | final URL url;
71 | final byte[] classfile;
72 |
73 | Info(URL url, byte[] classfile)
74 | {
75 | this.url = url;
76 | this.classfile = classfile;
77 | }
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/CyclicDependencyException.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire;
2 |
3 | public class CyclicDependencyException extends Exception
4 | {
5 |
6 | }
7 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/DownloadAndRestarter.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire;
2 |
3 | import java.io.File;
4 | import java.io.FileOutputStream;
5 | import java.io.IOException;
6 | import java.net.URISyntaxException;
7 | import java.net.URL;
8 | import java.nio.channels.Channels;
9 | import java.nio.channels.ReadableByteChannel;
10 | import java.util.ArrayList;
11 | import java.util.Arrays;
12 |
13 | public class DownloadAndRestarter
14 | {
15 | private DownloadAndRestarter() {}
16 |
17 | public static final boolean DO_APPEND = false;
18 |
19 | private static String fileNameFromURL(URL url)
20 | {
21 | String uri = url.toString();
22 | return uri.substring(uri.lastIndexOf('/') + 1, uri.length());
23 | }
24 |
25 | private static void restartApplication() throws URISyntaxException, IOException
26 | {
27 | final String javaBin = System.getProperty("java.home") + File.separator + "bin" + File.separator + "java";
28 | final File currentJar = new File(DownloadAndRestarter.class.getProtectionDomain().getCodeSource().getLocation().toURI());
29 |
30 | /* is it a jar file? */
31 | if (!currentJar.getName().endsWith(".jar")) {
32 | return;
33 | }
34 |
35 | /* Build command: java -jar application.jar */
36 | final ArrayList command = new ArrayList();
37 | command.add(javaBin);
38 | command.add("-jar");
39 | command.add(currentJar.getPath());
40 | command.addAll(Arrays.asList(Loader.ARGS));
41 |
42 | final ProcessBuilder builder = new ProcessBuilder(command);
43 | builder.start();
44 | System.exit(0);
45 | }
46 |
47 | public static void downloadOne(URL download) throws IOException
48 | {
49 | ReadableByteChannel rbc = Channels.newChannel(download.openStream());
50 | String fileName = fileNameFromURL(download);
51 | FileOutputStream fos = new FileOutputStream(Loader.MOD_DIR + fileName, DO_APPEND);
52 | fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
53 | fos.close();
54 | }
55 |
56 | public static void downloadAndRestart(URL[] downloads) throws IOException, URISyntaxException
57 | {
58 | for (URL download : downloads) {
59 | downloadOne(download);
60 | }
61 | restartApplication();
62 | }
63 |
64 | }
65 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/DuplicateModIDException.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire;
2 |
3 | public class DuplicateModIDException extends Exception
4 | {
5 | private ModInfo info1;
6 | private ModInfo info2;
7 |
8 | public DuplicateModIDException(ModInfo info1, ModInfo info2)
9 | {
10 | this.info1 = info1;
11 | this.info2 = info2;
12 | }
13 |
14 | @Override
15 | public String getMessage()
16 | {
17 | return "Duplicate Mod ID: '" + info1.ID + "'. Used by '" + info1.Name + "' and '" + info2.Name + "'.";
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/GameBetaFinder.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire;
2 |
3 | import org.objectweb.asm.*;
4 |
5 | public class GameBetaFinder extends ClassVisitor
6 | {
7 | public GameBetaFinder()
8 | {
9 | super(Opcodes.ASM5);
10 | // TODO?
11 | //super(classVisitor);
12 | }
13 |
14 | @Override
15 | public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions)
16 | {
17 | MethodVisitor r = super.visitMethod(access, name, desc, signature, exceptions);
18 | if (name.equals("")) {
19 | r = new MyMethodAdapter(r);
20 | }
21 | return r;
22 | }
23 |
24 | class MyMethodAdapter extends MethodVisitor
25 | {
26 | private boolean isBeta = false;
27 |
28 | MyMethodAdapter(MethodVisitor methodVisitor)
29 | {
30 | super(Opcodes.ASM5);
31 | // TODO?
32 | //super(methodVisitor);
33 | }
34 |
35 | @Override
36 | public void visitFieldInsn(int opcode, String owner, String name, String descriptor)
37 | {
38 | super.visitFieldInsn(opcode, owner, name, descriptor);
39 | if (name.equals("isBeta") && opcode == Opcodes.PUTSTATIC) {
40 | Loader.STS_BETA = isBeta;
41 | }
42 | }
43 |
44 | @Override
45 | public void visitInsn(int opcode)
46 | {
47 | super.visitInsn(opcode);
48 | if (opcode == Opcodes.ICONST_0) {
49 | isBeta = false;
50 | } else if (opcode == Opcodes.ICONST_1) {
51 | isBeta = true;
52 | }
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/GithubUpdateChecker.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire;
2 |
3 | import com.google.gson.*;
4 | import com.vdurmont.semver4j.Semver;
5 | import com.vdurmont.semver4j.SemverException;
6 |
7 | import javax.swing.*;
8 | import java.io.FileNotFoundException;
9 | import java.io.IOException;
10 | import java.io.InputStream;
11 | import java.io.InputStreamReader;
12 | import java.net.HttpURLConnection;
13 | import java.net.MalformedURLException;
14 | import java.net.URL;
15 |
16 | public class GithubUpdateChecker extends UpdateChecker
17 | {
18 | public GithubUpdateChecker(String username, String reponame) throws MalformedURLException
19 | {
20 | this("https://api.github.com/repos/" + username + "/" + reponame + "/releases/latest");
21 | }
22 |
23 | public GithubUpdateChecker(String url) throws MalformedURLException
24 | {
25 | super(url);
26 | }
27 |
28 | private static String removeLatestFromURL(String url)
29 | {
30 | if (url.endsWith("/latest")) {
31 | return url.substring(0, url.length() - "/latest".length());
32 | }
33 | return url;
34 | }
35 |
36 | @Override
37 | protected void obtainLatestRelease() throws IOException
38 | {
39 | // If no latest release exists because all releases are pre-releases, grab the latest pre-release
40 | try {
41 | super.obtainLatestRelease();
42 | } catch (FileNotFoundException e) {
43 | jsonURL = new URL(removeLatestFromURL(jsonURL.toString()));
44 |
45 | HttpURLConnection request = (HttpURLConnection) jsonURL.openConnection();
46 | request.connect();
47 |
48 | try {
49 | JsonParser jp = new JsonParser();
50 | JsonElement root = jp.parse(new InputStreamReader((InputStream) request.getContent()));
51 | latest = root.getAsJsonArray().get(0).getAsJsonObject();
52 | } catch (JsonSyntaxException e2) {
53 | System.out.println(jsonURL);
54 | System.out.println(e.getMessage());
55 | }
56 | }
57 | }
58 |
59 | @Override
60 | public Semver getLatestReleaseVersion() throws IOException
61 | {
62 | try {
63 | return ModInfo.safeVersion(getElementAsString("tag_name"));
64 | } catch (SemverException e) {
65 | JOptionPane.showMessageDialog(null,
66 | getElementAsString("html_url")
67 | + "\nrelease has a missing or bad version number: \""
68 | + getElementAsString("tag_name")
69 | + "\".\nGo yell at the author to fix it.",
70 | "Warning", JOptionPane.WARNING_MESSAGE);
71 | return null;
72 | }
73 | }
74 |
75 | @Override
76 | public URL getLatestReleaseURL() throws IOException
77 | {
78 | return new URL(getElementAsString("html_url"));
79 | }
80 |
81 | @Override
82 | public URL getLatestDownloadURL() throws IOException
83 | {
84 | JsonElement assets = getElement("assets");
85 |
86 | if (!(assets instanceof JsonArray)) {
87 | throw new RuntimeException("Excepted assets to be of type array");
88 | }
89 |
90 | for (JsonElement asset : (JsonArray)assets) {
91 | if (asset instanceof JsonObject) {
92 | JsonObject object = (JsonObject)asset;
93 | String url = object.get("browser_download_url").getAsString();
94 | if (isJar(url)) {
95 | return new URL(url);
96 | }
97 | }
98 | }
99 |
100 | throw new RuntimeException("Could not find jar asset to download");
101 | }
102 |
103 | private boolean isJar(String url) {
104 | return url.toLowerCase().endsWith(".jar");
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/MTSClassPool.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire;
2 |
3 | import javassist.ClassPath;
4 | import javassist.ClassPool;
5 | import javassist.CtClass;
6 | import javassist.LoaderClassPath;
7 |
8 | import java.util.ArrayList;
9 | import java.util.HashSet;
10 | import java.util.List;
11 | import java.util.Set;
12 |
13 | public class MTSClassPool extends ClassPool
14 | {
15 | private ClassLoader classLoader;
16 | private List classPaths = new ArrayList<>();
17 | private Set outJar = null;
18 |
19 | public MTSClassPool(MTSClassLoader classLoader)
20 | {
21 | appendSystemPath();
22 | resetClassLoader(classLoader);
23 | }
24 |
25 | public void resetClassLoader(MTSClassLoader loader)
26 | {
27 | classLoader = loader;
28 | for (ClassPath classPath : classPaths) {
29 | removeClassPath(classPath);
30 | }
31 | classPaths.clear();
32 |
33 | insertClassPath(new LoaderClassPath(loader));
34 | loader.addStreamToClassPool(this);
35 | }
36 |
37 | @Override
38 | public ClassPath insertClassPath(ClassPath cp)
39 | {
40 | classPaths.add(cp);
41 | return super.insertClassPath(cp);
42 | }
43 |
44 | @Override
45 | public ClassPath appendClassPath(ClassPath cp)
46 | {
47 | classPaths.add(cp);
48 | return super.appendClassPath(cp);
49 | }
50 |
51 | @Override
52 | public ClassLoader getClassLoader()
53 | {
54 | return classLoader;
55 | }
56 |
57 | public void setParent(ClassPool parent)
58 | {
59 | this.parent = parent;
60 | }
61 |
62 | public Set getModifiedClasses()
63 | {
64 | Set ret = new HashSet<>();
65 | for (Object v : classes.values()) {
66 | CtClass cls = (CtClass) v;
67 | if (cls.isModified()) {
68 | ret.add(cls);
69 | }
70 | }
71 | if (Loader.OUT_JAR || Loader.PACKAGE) {
72 | outJar = ret;
73 | }
74 | return ret;
75 | }
76 |
77 | public Set getOutJarClasses()
78 | {
79 | return outJar;
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/MissingDependencyException.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire;
2 |
3 | public class MissingDependencyException extends Exception
4 | {
5 | public ModInfo modInfo;
6 | public String dependency;
7 |
8 | public MissingDependencyException(ModInfo modInfo, String dependency)
9 | {
10 | this.modInfo = modInfo;
11 | this.dependency = dependency;
12 | }
13 |
14 | @Override
15 | public String getMessage()
16 | {
17 | String ret = "Unable to find dependency '" + dependency + "' for mod '";
18 | if (modInfo.ID == null || modInfo.ID.isEmpty()) {
19 | ret += modInfo.Name;
20 | } else {
21 | ret += modInfo.ID;
22 | }
23 | ret += "'.";
24 | return ret;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/MissingModIDException.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire;
2 |
3 | public class MissingModIDException extends Exception
4 | {
5 | public String modID;
6 |
7 | public MissingModIDException(String modID)
8 | {
9 | this.modID = modID;
10 | }
11 |
12 | @Override
13 | public String getMessage()
14 | {
15 | return "Unable to find Mod ID '" + modID + "'.";
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/ModUpdate.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire;
2 |
3 | import java.net.URL;
4 |
5 | public class ModUpdate
6 | {
7 | public ModInfo info;
8 | public URL releaseURL;
9 | public URL downloadURL;
10 |
11 | public ModUpdate(ModInfo info, URL releaseURL, URL downloadURL)
12 | {
13 | this.info = info;
14 | this.releaseURL = releaseURL;
15 | this.downloadURL = downloadURL;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/OutJar.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire;
2 |
3 | import javassist.CannotCompileException;
4 | import javassist.CtClass;
5 |
6 | import java.io.File;
7 | import java.io.FileOutputStream;
8 | import java.io.IOException;
9 | import java.util.ArrayList;
10 | import java.util.List;
11 | import java.util.Set;
12 | import java.util.jar.JarEntry;
13 | import java.util.jar.JarOutputStream;
14 |
15 | class OutJar
16 | {
17 | public static class FilePathAndBytes
18 | {
19 | public String path;
20 | public byte[] b;
21 |
22 | public FilePathAndBytes(String path, byte[] b)
23 | {
24 | this.path = path;
25 | this.b = b;
26 | }
27 | }
28 |
29 | /* https://stackoverflow.com/questions/22591903/javassist-how-to-inject-a-method-into-a-class-in-jar?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa */
30 | public static class JarHandler
31 | {
32 | public void writeOut(String jarPathAndName, List files)
33 | throws IOException
34 | {
35 | File jarFile = new File(jarPathAndName);
36 |
37 | try {
38 | JarOutputStream tempJar = new JarOutputStream(new FileOutputStream(jarFile));
39 |
40 | try {
41 | // Open the given file.
42 |
43 | try {
44 | // Create a jar entry and add it to the temp jar.
45 |
46 | for (FilePathAndBytes file : files) {
47 | String fileName = file.path;
48 | byte[] fileByteCode = file.b;
49 | JarEntry entry = new JarEntry(fileName);
50 | tempJar.putNextEntry(entry);
51 | tempJar.write(fileByteCode);
52 | }
53 |
54 | } catch (Exception ex) {
55 | System.out.println(ex);
56 |
57 | // Add a stub entry here, so that the jar will close
58 | // without an
59 | // exception.
60 |
61 | tempJar.putNextEntry(new JarEntry("stub"));
62 | }
63 |
64 | } catch (Exception ex) {
65 | System.out.println(ex);
66 |
67 | // IMportant so the jar will close without an
68 | // exception.
69 |
70 | tempJar.putNextEntry(new JarEntry("stub"));
71 | } finally {
72 | tempJar.close();
73 | }
74 | } finally {
75 | // do I need to do things here
76 | }
77 | }
78 | }
79 |
80 | public static void dumpJar(MTSClassPool pool, String jarPath)
81 | throws SecurityException, IllegalArgumentException, IOException
82 | {
83 | Set ctClasses = pool.getOutJarClasses();
84 | List files = new ArrayList<>();
85 | for (CtClass ctClass : ctClasses) {
86 | try {
87 | String className = ctClass.getName();
88 | byte[] b = ctClass.toBytecode();
89 | String classPath = className.replaceAll("\\.", "/") + ".class";
90 | files.add(new FilePathAndBytes(classPath, b));
91 | } catch (IOException | CannotCompileException e) {
92 | // eat it - just means this isn't a file we've loaded
93 | }
94 | }
95 | JarHandler handler = new JarHandler();
96 | handler.writeOut(jarPath, files);
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/ReflectionHelper.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire;
2 |
3 | import sun.reflect.*;
4 | import java.lang.reflect.*;
5 |
6 | public class ReflectionHelper {
7 | private static final String MODIFIERS_FIELD = "modifiers";
8 |
9 | private static final ReflectionFactory reflection =
10 | ReflectionFactory.getReflectionFactory();
11 |
12 | public static void setStaticFinalField(
13 | Field field, Object value)
14 | throws NoSuchFieldException, IllegalAccessException {
15 | // we mark the field to be public
16 | field.setAccessible(true);
17 | // next we change the modifier in the Field instance to
18 | // not be final anymore, thus tricking reflection into
19 | // letting us modify the static final field
20 | Field modifiersField =
21 | Field.class.getDeclaredField(MODIFIERS_FIELD);
22 | modifiersField.setAccessible(true);
23 | int modifiers = modifiersField.getInt(field);
24 | // blank out the final bit in the modifiers int
25 | modifiers &= ~Modifier.FINAL;
26 | modifiersField.setInt(field, modifiers);
27 | FieldAccessor fa = reflection.newFieldAccessor(
28 | field, false
29 | );
30 | fa.set(null, value);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/finders/InOrderFinder.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire.finders;
2 |
3 | import java.util.List;
4 |
5 | import com.evacipated.cardcrawl.modthespire.lib.Matcher;
6 | import com.evacipated.cardcrawl.modthespire.patcher.Expectation;
7 |
8 | import javassist.expr.Expr;
9 |
10 | public class InOrderFinder extends MatchFinderExprEditor {
11 |
12 | private int location;
13 | private boolean foundLocation;
14 | private int foundMatchesIndex;
15 |
16 | private Matcher finalMatch;
17 | private List expectedMatches;
18 |
19 | public InOrderFinder(List expectedMatches, Matcher finalMatch) {
20 | super();
21 |
22 | this.expectedMatches = expectedMatches;
23 | this.finalMatch = finalMatch;
24 |
25 | this.foundMatchesIndex = 0;
26 | this.foundLocation = false;
27 | }
28 |
29 | private void foundFinalMatch(int lineNumber) {
30 | if (foundLocation) return;
31 |
32 | this.foundLocation = true;
33 | this.location = lineNumber;
34 | }
35 |
36 | private boolean finalMatch() {
37 | return foundMatchesIndex >= expectedMatches.size();
38 | }
39 |
40 | private void foundMatch() {
41 | this.foundMatchesIndex++;
42 | }
43 |
44 | private Matcher currentMatch() {
45 | return expectedMatches.get(foundMatchesIndex);
46 | }
47 |
48 | @Override
49 | protected void doMatch(Expectation expectedType, Expr toMatch) {
50 | if (finalMatch()) {
51 | if (finalMatch.getExpectation() == expectedType && finalMatch.match(toMatch)) {
52 | foundFinalMatch(toMatch.getLineNumber());
53 | }
54 | } else {
55 | Matcher current = currentMatch();
56 | if (current.getExpectation() == expectedType && current.match(toMatch)) {
57 | foundMatch();
58 | }
59 | }
60 | }
61 |
62 | @Override
63 | public boolean didFindLocation() {
64 | return foundLocation;
65 | }
66 |
67 | @Override
68 | public int[] getFoundLocations() {
69 | return new int[] {location};
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/finders/InOrderMultiFinder.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire.finders;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | import com.evacipated.cardcrawl.modthespire.lib.Matcher;
7 | import com.evacipated.cardcrawl.modthespire.patcher.Expectation;
8 |
9 | import javassist.expr.Expr;
10 |
11 | public class InOrderMultiFinder extends MatchFinderExprEditor {
12 |
13 | private List locations;
14 | private boolean foundLocation;
15 | private int foundMatchesIndex;
16 |
17 | private Matcher finalMatch;
18 | private List expectedMatches;
19 |
20 | public InOrderMultiFinder(List expectedMatches, Matcher finalMatch) {
21 | super();
22 |
23 | this.expectedMatches = expectedMatches;
24 | this.finalMatch = finalMatch;
25 |
26 | this.foundMatchesIndex = 0;
27 | this.foundLocation = false;
28 | this.locations = new ArrayList<>();
29 | }
30 |
31 | private void foundFinalMatch(int lineNumber) {
32 | this.foundLocation = true;
33 | this.locations.add(lineNumber);
34 | }
35 |
36 | private boolean finalMatch() {
37 | return foundMatchesIndex >= expectedMatches.size();
38 | }
39 |
40 | private void foundMatch() {
41 | this.foundMatchesIndex++;
42 | }
43 |
44 | private Matcher currentMatch() {
45 | return expectedMatches.get(foundMatchesIndex);
46 | }
47 |
48 | @Override
49 | protected void doMatch(Expectation expectedType, Expr toMatch) {
50 | if (finalMatch()) {
51 | if (finalMatch.getExpectation() == expectedType && finalMatch.match(toMatch)) {
52 | foundFinalMatch(toMatch.getLineNumber());
53 | }
54 | } else {
55 | Matcher current = currentMatch();
56 | if (current.getExpectation() == expectedType && current.match(toMatch)) {
57 | foundMatch();
58 | }
59 | }
60 | }
61 |
62 | @Override
63 | public boolean didFindLocation() {
64 | return foundLocation;
65 | }
66 |
67 | @Override
68 | public int[] getFoundLocations() {
69 | int[] asArray = new int[locations.size()];
70 | for (int i = 0; i < asArray.length; i++) {
71 | asArray[i] = locations.get(i);
72 | }
73 | return asArray;
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/finders/MatchFinderExprEditor.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire.finders;
2 |
3 | import com.evacipated.cardcrawl.modthespire.patcher.Expectation;
4 |
5 | import javassist.expr.Cast;
6 | import javassist.expr.ConstructorCall;
7 | import javassist.expr.Expr;
8 | import javassist.expr.ExprEditor;
9 | import javassist.expr.FieldAccess;
10 | import javassist.expr.Handler;
11 | import javassist.expr.Instanceof;
12 | import javassist.expr.MethodCall;
13 | import javassist.expr.NewArray;
14 | import javassist.expr.NewExpr;
15 |
16 | public abstract class MatchFinderExprEditor extends ExprEditor {
17 |
18 | protected abstract void doMatch(Expectation expectedType, Expr toMatch);
19 |
20 | public abstract boolean didFindLocation();
21 |
22 | public abstract int[] getFoundLocations();
23 |
24 | @Override
25 | public void edit(Cast expr) {
26 | doMatch(Expectation.TYPE_CAST, expr);
27 | }
28 |
29 | @Override
30 | public void edit(ConstructorCall expr) {
31 | doMatch(Expectation.CONSTRUCTOR_CALL, expr);
32 | }
33 |
34 | @Override
35 | public void edit(FieldAccess expr) {
36 | doMatch(Expectation.FIELD_ACCESS, expr);
37 | }
38 |
39 | @Override
40 | public void edit(Handler expr) {
41 | doMatch(Expectation.CATCH_CLAUSE, expr);
42 | }
43 |
44 | @Override
45 | public void edit(Instanceof expr) {
46 | doMatch(Expectation.INSTANCEOF, expr);
47 | }
48 |
49 | @Override
50 | public void edit(MethodCall expr) {
51 | doMatch(Expectation.METHOD_CALL, expr);
52 | }
53 |
54 | @Override
55 | public void edit(NewArray expr) {
56 | doMatch(Expectation.ARRAY_CREATION, expr);
57 | }
58 |
59 | @Override
60 | public void edit(NewExpr expr) {
61 | doMatch(Expectation.NEW_EXPRESSION, expr);
62 | }
63 |
64 | }
65 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/lib/ByRef.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire.lib;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | @Target({ElementType.PARAMETER})
9 | @Retention(RetentionPolicy.RUNTIME)
10 | public @interface ByRef {
11 | String type() default "";
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/lib/ConfigUtils.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire.lib;
2 |
3 | import org.apache.commons.lang3.SystemUtils;
4 |
5 | import java.io.File;
6 |
7 | public class ConfigUtils
8 | {
9 | private static final String APP_NAME = "ModTheSpire";
10 | public static final String CONFIG_DIR;
11 |
12 | static {
13 | String basedir;
14 | if (SystemUtils.IS_OS_WINDOWS) {
15 | // %LOCALAPPDATA%/APP_NAME/
16 | // Fallback to %APPDATA%/APP_NAME/
17 | String appdata = System.getenv("LOCALAPPDATA");
18 | if (appdata == null || appdata.isEmpty()) {
19 | appdata = System.getenv("APPDATA");
20 | }
21 | basedir = appdata;
22 | } else if (SystemUtils.IS_OS_LINUX) {
23 | // /home/x/.config/APP_NAME/
24 | basedir = SystemUtils.USER_HOME + File.separator
25 | + ".config" + File.separator;
26 | } else if (SystemUtils.IS_OS_MAC) {
27 | // /Users/x/Library/Preferences/APP_NAME/
28 | basedir = SystemUtils.USER_HOME + File.separator
29 | + "Library" + File.separator
30 | + "Preferences" + File.separator;
31 | } else {
32 | // user.home/APP_NAME/
33 | basedir = SystemUtils.USER_HOME;
34 | }
35 | CONFIG_DIR = basedir + File.separator +
36 | APP_NAME;
37 |
38 | // Make config directory
39 | File directory = new File(CONFIG_DIR);
40 | directory.mkdirs();
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/lib/LineFinder.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire.lib;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | import com.evacipated.cardcrawl.modthespire.finders.InOrderFinder;
7 | import com.evacipated.cardcrawl.modthespire.finders.InOrderMultiFinder;
8 | import com.evacipated.cardcrawl.modthespire.finders.MatchFinderExprEditor;
9 | import com.evacipated.cardcrawl.modthespire.patcher.PatchingException;
10 |
11 | import javassist.CannotCompileException;
12 | import javassist.CtBehavior;
13 |
14 | public class LineFinder {
15 |
16 | private LineFinder() {}
17 |
18 | public static final int[] findAllInOrder(CtBehavior ctMethodToPatch, Matcher finalMatch) throws CannotCompileException, PatchingException {
19 | return findAllInOrder(ctMethodToPatch, new ArrayList<>(), finalMatch);
20 | }
21 |
22 | public static final int[] findAllInOrder(CtBehavior ctMethodToPatch, List expectedMatches, Matcher finalMatch) throws CannotCompileException, PatchingException {
23 | MatchFinderExprEditor editor = new InOrderMultiFinder(expectedMatches, finalMatch);
24 | ctMethodToPatch.instrument(editor);
25 | if (!editor.didFindLocation()) {
26 | throw new PatchingException(ctMethodToPatch, "Location matching given description could not be found for patch");
27 | }
28 | return editor.getFoundLocations();
29 | }
30 |
31 | public static final int[] findInOrder(CtBehavior ctMethodToPatch, Matcher finalMatch) throws CannotCompileException, PatchingException {
32 | return findInOrder(ctMethodToPatch, new ArrayList<>(), finalMatch);
33 | }
34 |
35 | public static final int[] findInOrder(CtBehavior ctMethodToPatch, List expectedMatches, Matcher finalMatch) throws CannotCompileException, PatchingException {
36 | MatchFinderExprEditor editor = new InOrderFinder(expectedMatches, finalMatch);
37 | ctMethodToPatch.instrument(editor);
38 | if (!editor.didFindLocation()) {
39 | throw new PatchingException(ctMethodToPatch, "Location matching given description could not be found for patch");
40 | }
41 | return editor.getFoundLocations();
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/lib/SpireConfig.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire.lib;
2 |
3 | import java.io.*;
4 | import java.util.Properties;
5 |
6 | public class SpireConfig
7 | {
8 | private final static String EXTENSION = "properties";
9 | private Properties properties;
10 | private File file;
11 | private String filePath;
12 |
13 | public static String makeFilePath(String modName, String fileName)
14 | {
15 | return makeFilePath(modName, fileName, EXTENSION);
16 | }
17 |
18 | public static String makeFilePath(String modName, String fileName, String ext)
19 | {
20 | String dirPath;
21 | if (modName == null) {
22 | dirPath = ConfigUtils.CONFIG_DIR + File.separator;
23 | } else {
24 | dirPath = ConfigUtils.CONFIG_DIR + File.separator
25 | + modName + File.separator;
26 | }
27 | String filePath = dirPath + fileName + "." + ext;
28 | File dir = new File(dirPath);
29 | dir.mkdirs();
30 |
31 | return filePath;
32 | }
33 |
34 | public SpireConfig(String modName, String fileName) throws IOException
35 | {
36 | this(modName, fileName, new Properties());
37 | }
38 |
39 | public SpireConfig(String modName, String fileName, Properties defaultProperties) throws IOException
40 | {
41 | properties = new Properties(defaultProperties);
42 |
43 | filePath = makeFilePath(modName, fileName);
44 |
45 | file = new File(filePath);
46 | file.createNewFile();
47 | load();
48 | }
49 |
50 | public void load() throws IOException
51 | {
52 | properties.load(new FileInputStream(file));
53 | }
54 |
55 | public void save() throws IOException
56 | {
57 | properties.store(new FileOutputStream(file), null);
58 | }
59 |
60 | public boolean has(String key)
61 | {
62 | return properties.containsKey(key);
63 | }
64 |
65 | public void remove(String key)
66 | {
67 | properties.remove(key);
68 | }
69 |
70 | public void clear()
71 | {
72 | properties.clear();
73 | }
74 |
75 | public String getString(String key)
76 | {
77 | return properties.getProperty(key);
78 | }
79 |
80 | public void setString(String key, String value)
81 | {
82 | properties.setProperty(key, value);
83 | }
84 |
85 | public boolean getBool(String key)
86 | {
87 | return Boolean.parseBoolean(getString(key));
88 | }
89 |
90 | public void setBool(String key, boolean value)
91 | {
92 | setString(key, Boolean.toString(value));
93 | }
94 |
95 | public int getInt(String key)
96 | {
97 | return Integer.parseInt(getString(key));
98 | }
99 |
100 | public void setInt(String key, int value)
101 | {
102 | setString(key, Integer.toString(value));
103 | }
104 |
105 | public float getFloat(String key)
106 | {
107 | return Float.parseFloat(getString(key));
108 | }
109 |
110 | public void setFloat(String key, float value)
111 | {
112 | setString(key, Float.toString(value));
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/lib/SpireEnum.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire.lib;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | @Target({ElementType.FIELD})
9 | @Retention(RetentionPolicy.RUNTIME)
10 | public @interface SpireEnum {
11 | String name() default "";
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/lib/SpireField.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire.lib;
2 |
3 | import java.lang.reflect.Field;
4 |
5 | public class SpireField
6 | {
7 | public interface DefaultValue
8 | {
9 | T get();
10 | }
11 |
12 | private DefaultValue defaultValue;
13 |
14 | private Field field;
15 |
16 | public SpireField(DefaultValue defaultValue)
17 | {
18 | this.defaultValue = defaultValue;
19 | }
20 |
21 | public SpireField(SpireField originalSpireField)
22 | {
23 | if (originalSpireField != null) {
24 | defaultValue = originalSpireField.defaultValue;
25 | }
26 | }
27 |
28 | public void initialize(Class clz, String fieldName) throws NoSuchFieldException
29 | {
30 | field = clz.getDeclaredField(fieldName);
31 | field.setAccessible(true);
32 | }
33 |
34 | public T getDefaultValue()
35 | {
36 | return defaultValue.get();
37 | }
38 |
39 | public T get(Object __instance)
40 | {
41 | // This should never be called, but serves as a fallback
42 | try {
43 | return (T) field.get(__instance);
44 | } catch (IllegalAccessException e) {
45 | e.printStackTrace();
46 | }
47 | return null;
48 | }
49 |
50 | public void set(Object __instance, T value)
51 | {
52 | // This should never be called, but serves as a fallback
53 | try {
54 | field.set(__instance, value);
55 | } catch (IllegalAccessException e) {
56 | e.printStackTrace();
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/lib/SpireInitializer.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire.lib;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | @Target({ElementType.TYPE})
9 | @Retention(RetentionPolicy.RUNTIME)
10 | public @interface SpireInitializer {
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/lib/SpireInsertLocator.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire.lib;
2 |
3 | import javassist.CtBehavior;
4 |
5 | public abstract class SpireInsertLocator
6 | {
7 | public abstract int[] Locate(CtBehavior ctMethodToPatch) throws Exception;
8 | }
9 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/lib/SpireInsertPatch.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire.lib;
2 |
3 | import javassist.CtBehavior;
4 |
5 | import java.lang.annotation.ElementType;
6 | import java.lang.annotation.Retention;
7 | import java.lang.annotation.RetentionPolicy;
8 | import java.lang.annotation.Target;
9 |
10 | @Target({ElementType.METHOD})
11 | @Retention(RetentionPolicy.RUNTIME)
12 | public @interface SpireInsertPatch {
13 | Class extends SpireInsertLocator> locator() default NONE.class;
14 | int loc() default -1;
15 | int rloc() default -1;
16 | int[] locs() default {};
17 | int[] rlocs() default {};
18 | String[] localvars() default {};
19 |
20 | final class NONE extends SpireInsertLocator
21 | {
22 | @Override
23 | public int[] Locate(CtBehavior ctMethodToPatch) throws Exception
24 | {
25 | return null;
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/lib/SpireInstrumentPatch.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire.lib;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | @Target({ElementType.METHOD})
9 | @Retention(RetentionPolicy.RUNTIME)
10 | public @interface SpireInstrumentPatch { }
11 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/lib/SpireOverride.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire.lib;
2 |
3 | import java.lang.annotation.*;
4 |
5 | @Target({ElementType.METHOD})
6 | @Retention(RetentionPolicy.RUNTIME)
7 | public @interface SpireOverride
8 | {
9 | }
10 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/lib/SpirePatch.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire.lib;
2 |
3 | import java.lang.annotation.*;
4 |
5 | @Repeatable(SpirePatches.class)
6 | @Target({ElementType.TYPE})
7 | @Retention(RetentionPolicy.RUNTIME)
8 | public @interface SpirePatch
9 | {
10 | Class> clz() default void.class;
11 | String cls() default "";
12 | String method();
13 | Class>[] paramtypez() default {void.class};
14 | String[] paramtypes() default {"DEFAULT"};
15 | String requiredModId() default "";
16 | boolean optional() default false;
17 |
18 | String CONSTRUCTOR = "";
19 | String STATICINITIALIZER = "";
20 | String CLASS = "";
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/lib/SpirePatch2.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire.lib;
2 |
3 | import java.lang.annotation.*;
4 |
5 | @Repeatable(SpirePatches2.class)
6 | @Target({ElementType.TYPE})
7 | @Retention(RetentionPolicy.RUNTIME)
8 | public @interface SpirePatch2
9 | {
10 | Class> clz() default void.class;
11 | String cls() default "";
12 | String method();
13 | Class>[] paramtypez() default {void.class};
14 | String[] paramtypes() default {"DEFAULT"};
15 | String requiredModId() default "";
16 | boolean optional() default false;
17 | }
18 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/lib/SpirePatches.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire.lib;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | @Target({ElementType.TYPE})
9 | @Retention(RetentionPolicy.RUNTIME)
10 | public @interface SpirePatches
11 | {
12 | SpirePatch[] value();
13 | }
14 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/lib/SpirePatches2.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire.lib;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | @Target({ElementType.TYPE})
9 | @Retention(RetentionPolicy.RUNTIME)
10 | public @interface SpirePatches2
11 | {
12 | SpirePatch2[] value();
13 | }
14 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/lib/SpirePostfixPatch.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire.lib;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | @Target({ElementType.METHOD})
9 | @Retention(RetentionPolicy.RUNTIME)
10 | public @interface SpirePostfixPatch
11 | {
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/lib/SpirePrefixPatch.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire.lib;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | @Target({ElementType.METHOD})
9 | @Retention(RetentionPolicy.RUNTIME)
10 | public @interface SpirePrefixPatch
11 | {
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/lib/SpireRawPatch.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire.lib;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | @Target({ElementType.METHOD})
9 | @Retention(RetentionPolicy.RUNTIME)
10 | public @interface SpireRawPatch { }
11 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/lib/SpireReturn.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire.lib;
2 |
3 | import java.util.NoSuchElementException;
4 |
5 | public final class SpireReturn
6 | {
7 | private static final SpireReturn> EMPTY = new SpireReturn<>();
8 | private static final SpireReturn PLACEHOLDER = new SpireReturn<>(null);
9 |
10 | private final boolean hasValue;
11 | private T value;
12 |
13 | private SpireReturn()
14 | {
15 | hasValue = false;
16 | value = null;
17 | }
18 |
19 | public static SpireReturn Continue()
20 | {
21 | @SuppressWarnings("unchecked")
22 | SpireReturn t = (SpireReturn) EMPTY;
23 | return t;
24 | }
25 |
26 | private SpireReturn(T value)
27 | {
28 | hasValue = true;
29 | this.value = value;
30 | }
31 |
32 | public static SpireReturn Return(T value)
33 | {
34 | PLACEHOLDER.value = value;
35 | @SuppressWarnings("unchecked")
36 | SpireReturn ret = (SpireReturn) PLACEHOLDER;
37 | return ret;
38 | }
39 |
40 | public static SpireReturn Return()
41 | {
42 | return Return(null);
43 | }
44 |
45 | public T get()
46 | {
47 | if (!isPresent()) {
48 | throw new NoSuchElementException("No value present");
49 | }
50 | return value;
51 | }
52 |
53 | public boolean isPresent()
54 | {
55 | return hasValue;
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/lib/SpireSideload.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire.lib;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | @Target({ElementType.TYPE})
9 | @Retention(RetentionPolicy.RUNTIME)
10 | public @interface SpireSideload
11 | {
12 | String[] modIDs();
13 | }
14 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/lib/SpireSuper.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire.lib;
2 |
3 | import org.apache.commons.lang3.NotImplementedException;
4 |
5 | public class SpireSuper
6 | {
7 | public static R call(Object... params)
8 | {
9 | throw new NotImplementedException("This shouldn't happen.");
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/lib/StaticSpireField.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire.lib;
2 |
3 | public class StaticSpireField extends SpireField
4 | {
5 | public StaticSpireField(DefaultValue defaultValue)
6 | {
7 | super(defaultValue);
8 | }
9 |
10 | public StaticSpireField(SpireField originalSpireField)
11 | {
12 | super(originalSpireField);
13 | }
14 |
15 | public T get()
16 | {
17 | return get(null);
18 | }
19 |
20 | public void set(T value)
21 | {
22 | set(null, value);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/patcher/ByRefParameterNotArrayException.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire.patcher;
2 |
3 | public class ByRefParameterNotArrayException extends PatchingException
4 | {
5 | public ByRefParameterNotArrayException(int index)
6 | {
7 | super(Integer.toString(index));
8 | }
9 |
10 | public ByRefParameterNotArrayException(String name)
11 | {
12 | super(name);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/patcher/Expectation.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire.patcher;
2 |
3 | public enum Expectation {
4 | TYPE_CAST, CONSTRUCTOR_CALL, FIELD_ACCESS,
5 | CATCH_CLAUSE, INSTANCEOF, METHOD_CALL,
6 | ARRAY_CREATION, NEW_EXPRESSION
7 | }
8 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/patcher/InstrumentPatchInfo.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire.patcher;
2 |
3 | import javassist.CannotCompileException;
4 | import javassist.CtBehavior;
5 | import javassist.expr.ExprEditor;
6 |
7 | import java.lang.reflect.InvocationTargetException;
8 | import java.lang.reflect.Method;
9 |
10 | public class InstrumentPatchInfo extends PatchInfo
11 | {
12 | private Method method;
13 |
14 | public InstrumentPatchInfo(CtBehavior ctMethodToPatch, Method method)
15 | {
16 | super(ctMethodToPatch, null);
17 | this.method = method;
18 | }
19 |
20 | @Override
21 | protected String patchClassName()
22 | {
23 | return method.getDeclaringClass().getName();
24 | }
25 |
26 | @Override
27 | protected String debugMsg()
28 | {
29 | return "Adding Instrument...";
30 | }
31 |
32 | @Override
33 | public int patchOrdering()
34 | {
35 | return -1;
36 | }
37 |
38 | @Override
39 | public void doPatch() throws PatchingException
40 | {
41 | try {
42 | ExprEditor exprEditor = (ExprEditor) method.invoke(null);
43 | ctMethodToPatch.instrument(exprEditor);
44 | } catch (IllegalAccessException | CannotCompileException | InvocationTargetException e) {
45 | throw new PatchingException(e);
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/patcher/LocatorInfo.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire.patcher;
2 |
3 | import com.evacipated.cardcrawl.modthespire.lib.SpireInsertLocator;
4 | import javassist.CtBehavior;
5 |
6 | import java.lang.reflect.Constructor;
7 |
8 | public class LocatorInfo {
9 |
10 | private CtBehavior ctMethodToPatch;
11 | private Class> finderClass;
12 |
13 | public LocatorInfo(CtBehavior ctMethodToPatch, Class> finderClass) {
14 | this.ctMethodToPatch = ctMethodToPatch;
15 | this.finderClass = finderClass;
16 | }
17 |
18 | public int[] findLines() throws Exception {
19 | Constructor> ctor = finderClass.getDeclaredConstructor();
20 | ctor.setAccessible(true);
21 | SpireInsertLocator obj = (SpireInsertLocator)ctor.newInstance();
22 | return obj.Locate(ctMethodToPatch);
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/patcher/MissingParamTypesException.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire.patcher;
2 |
3 | import com.evacipated.cardcrawl.modthespire.lib.SpirePatch;
4 | import javassist.CtClass;
5 |
6 | public class MissingParamTypesException extends Exception
7 | {
8 | public MissingParamTypesException(CtClass patchClass, SpirePatch patch)
9 | {
10 | super(String.format("Patch %s\nPatching %s.%s:\nHas overloads and no paramtypes defined", patchClass.getName(), patch.cls(), patch.method()));
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/patcher/NonStaticPatchMethodException.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire.patcher;
2 |
3 | import javassist.CtMethod;
4 |
5 | public class NonStaticPatchMethodException extends Exception
6 | {
7 | public NonStaticPatchMethodException(CtMethod patchMethod)
8 | {
9 | super(patchMethod.getLongName());
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/patcher/ParamInfo.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire.patcher;
2 |
3 | import com.evacipated.cardcrawl.modthespire.lib.ByRef;
4 | import javassist.CtBehavior;
5 | import javassist.CtClass;
6 | import javassist.Modifier;
7 | import javassist.NotFoundException;
8 | import javassist.bytecode.LocalVariableAttribute;
9 | import javassist.bytecode.MethodInfo;
10 |
11 | class ParamInfo
12 | {
13 | private CtBehavior ctBehavior;
14 | private int position;
15 | private String name;
16 | private boolean isPrivate = false;
17 |
18 | private static String findName(CtBehavior ctBehavior, int position)
19 | {
20 | MethodInfo methodInfo = ctBehavior.getMethodInfo2();
21 | LocalVariableAttribute table = (LocalVariableAttribute) methodInfo.getCodeAttribute().getAttribute(LocalVariableAttribute.tag);
22 | if (table != null) {
23 | int j = 0;
24 | for (int i=0; i ctBehavior.getParameterTypes().length) {
50 | this.position = -1;
51 | name = null;
52 | }
53 | } catch (NotFoundException e) {
54 | e.printStackTrace();
55 | }
56 |
57 | if (name != null) {
58 | if (name.startsWith("___")) {
59 | isPrivate = true;
60 | name = name.replaceFirst("___", "");
61 | }
62 | }
63 | }
64 |
65 | boolean isPrivateCapture()
66 | {
67 | return isPrivate;
68 | }
69 |
70 | CtClass getPrivateCaptureType(ParamInfo destInfo) throws PatchingException
71 | {
72 | if (!destInfo.isPrivateCapture()) {
73 | throw new PatchingException("Not a private capture, this shouldn't have been called");
74 | }
75 |
76 | CtClass declaringClass = ctBehavior.getDeclaringClass();
77 | try {
78 | return declaringClass.getDeclaredField(destInfo.getName()).getType();
79 | } catch (NotFoundException e) {
80 | return null;
81 | }
82 | }
83 |
84 | CtClass getDestByRefType(ParamInfo destInfo) throws ClassNotFoundException
85 | {
86 | String typename = null;
87 | for (Object o : destInfo.getAnnotations()) {
88 | if (o instanceof ByRef && !((ByRef) o).type().isEmpty()) {
89 | typename = ((ByRef) o).type();
90 | }
91 | }
92 |
93 | try {
94 | return ctBehavior.getDeclaringClass().getClassPool().get(typename);
95 | } catch (NotFoundException e) {
96 | return null;
97 | }
98 | }
99 |
100 | int getParamCount()
101 | {
102 | return ctBehavior.getAvailableParameterAnnotations().length;
103 | }
104 |
105 | int getPosition()
106 | {
107 | return position;
108 | }
109 |
110 | String getName()
111 | {
112 | return name;
113 | }
114 |
115 | CtClass getType() throws NotFoundException
116 | {
117 | if (position < 0) {
118 | return null;
119 | }
120 | if (position == 0) {
121 | return ctBehavior.getDeclaringClass();
122 | }
123 | return ctBehavior.getParameterTypes()[position - 1];
124 | }
125 |
126 | String getTypename() throws NotFoundException
127 | {
128 | CtClass type = getType();
129 | if (type == null) {
130 | return "";
131 | }
132 | return type.getName();
133 | }
134 |
135 | Object[] getAnnotations() throws ClassNotFoundException
136 | {
137 | return ctBehavior.getParameterAnnotations()[position - 1];
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/patcher/PatchInfo.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire.patcher;
2 |
3 | import com.evacipated.cardcrawl.modthespire.lib.ByRef;
4 | import com.evacipated.cardcrawl.modthespire.lib.SpirePatch;
5 | import com.evacipated.cardcrawl.modthespire.lib.SpirePatch2;
6 | import javassist.*;
7 | import javassist.bytecode.annotation.AnnotationImpl;
8 |
9 | import java.lang.reflect.Proxy;
10 |
11 | public abstract class PatchInfo
12 | {
13 | private static int modNum = 0;
14 |
15 | private int modOrder;
16 | SpirePatch patch;
17 | CtBehavior ctMethodToPatch;
18 | CtMethod patchMethod;
19 |
20 | public PatchInfo(CtBehavior ctMethodToPatch, CtMethod patchMethod)
21 | {
22 | this.ctMethodToPatch = ctMethodToPatch;
23 | this.patchMethod = patchMethod;
24 | this.modOrder = modNum;
25 | }
26 |
27 | public PatchInfo setSpirePatch(SpirePatch patch)
28 | {
29 | this.patch = patch;
30 | return this;
31 | }
32 |
33 | public void debugPrint()
34 | {
35 | System.out.println("Patch Class: [" + patchClassName() + "]");
36 | if (isSpirePatch2()) {
37 | System.out.println(" - SpirePatch2");
38 | }
39 | System.out.println(" - Patching [" + ctMethodToPatch.getLongName() + "]");
40 | System.out.print(" - ");
41 | System.out.println(debugMsg());
42 | }
43 |
44 | protected String patchClassName()
45 | {
46 | return patchMethod.getDeclaringClass().getName();
47 | }
48 |
49 | protected abstract String debugMsg();
50 |
51 | public static void nextMod()
52 | {
53 | ++modNum;
54 | }
55 |
56 | final public int modOrdering()
57 | {
58 | return modOrder;
59 | }
60 |
61 | final public boolean isSpirePatch2()
62 | {
63 | if (patch == null) {
64 | return false;
65 | }
66 |
67 | try {
68 | AnnotationImpl impl = (AnnotationImpl) Proxy.getInvocationHandler(patch);
69 | return SpirePatch2.class.getName().equals(impl.getTypeName());
70 | } catch (IllegalArgumentException | ClassCastException ignored) {
71 | return false;
72 | }
73 | }
74 |
75 | // Lower is earlier
76 | public abstract int patchOrdering();
77 |
78 | public abstract void doPatch() throws PatchingException;
79 |
80 | protected static boolean paramByRef(Object[] annotations)
81 | {
82 | for (Object o : annotations) {
83 | if (o instanceof ByRef) {
84 | return true;
85 | }
86 | }
87 | return false;
88 | }
89 |
90 | // Gets the typename from the ByRef annotation
91 | protected static String paramByRefTypename(Object[] annotations)
92 | {
93 | for (Object o : annotations) {
94 | if (o instanceof ByRef) {
95 | return ((ByRef) o).type();
96 | }
97 | }
98 | return "";
99 | }
100 |
101 | // Gets the typename from the patched method's marameter types
102 | protected static String paramByRefTypename2(CtBehavior ctMethodToPatch, int index) throws NotFoundException
103 | {
104 | if (!Modifier.isStatic(ctMethodToPatch.getModifiers())) {
105 | --index;
106 | }
107 | try {
108 | return ctMethodToPatch.getParameterTypes()[index].getName();
109 | } catch (ArrayIndexOutOfBoundsException e) {
110 | return null;
111 | }
112 | }
113 |
114 | protected static String paramByRefTypenamePrivateCapture(CtBehavior ctMethodToPatch, String paramName) throws NotFoundException
115 | {
116 | CtClass ctClass = ctMethodToPatch.getDeclaringClass();
117 | CtField ctField = ctClass.getField(paramName);
118 | return ctField.getType().getName();
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/patcher/PatchInfoComparator.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire.patcher;
2 |
3 | import java.util.Comparator;
4 |
5 | public class PatchInfoComparator implements Comparator
6 | {
7 | // Ordering:
8 | // Insert, Instrument, Replace, Prefix, Postfix, Raw
9 | // Then sorted by mod load order
10 | @Override
11 | public int compare(PatchInfo o1, PatchInfo o2)
12 | {
13 | int patchOrdering = Integer.compare(o1.patchOrdering(), o2.patchOrdering());
14 | if (patchOrdering != 0) {
15 | return patchOrdering;
16 | }
17 |
18 | int modOrdering = Integer.compare(o1.modOrdering(), o2.modOrdering());
19 | if (modOrdering != 0) {
20 | return modOrdering;
21 | }
22 |
23 | return o1.toString().compareTo(o2.toString());
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/patcher/PatchingException.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire.patcher;
2 |
3 | import javassist.CtBehavior;
4 | import javassist.CtClass;
5 |
6 | public class PatchingException extends Exception
7 | {
8 | public PatchingException(String msg)
9 | {
10 | super(msg);
11 | }
12 |
13 | public PatchingException(String msg, Throwable cause)
14 | {
15 | super(msg, cause);
16 | }
17 |
18 | public PatchingException(Throwable cause)
19 | {
20 | super(cause);
21 | }
22 |
23 | public PatchingException(CtClass ctClass, String msg)
24 | {
25 | super(ctClass.getName() + ": " + msg);
26 | }
27 |
28 | public PatchingException(CtBehavior m, String msg)
29 | {
30 | super(m.getLongName() + ": " + msg);
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/patcher/PrefixPatchInfo.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire.patcher;
2 |
3 | import javassist.CannotCompileException;
4 | import javassist.CtBehavior;
5 | import javassist.CtConstructor;
6 | import javassist.CtMethod;
7 |
8 | public class PrefixPatchInfo extends ParameterPatchInfo
9 | {
10 | public PrefixPatchInfo(CtBehavior ctMethodToPatch, CtMethod patchMethod)
11 | {
12 | super(ctMethodToPatch, patchMethod);
13 | canSpireReturn = true;
14 | canByRefParams = true;
15 | }
16 |
17 | @Override
18 | protected String debugMsg()
19 | {
20 | return "Adding Prefix...";
21 | }
22 |
23 | @Override
24 | public int patchOrdering()
25 | {
26 | return 1;
27 | }
28 |
29 | @Override
30 | protected void applyPatch(String src) throws CannotCompileException
31 | {
32 | if (ctMethodToPatch instanceof CtConstructor && !((CtConstructor) ctMethodToPatch).isClassInitializer()) {
33 | ((CtConstructor) ctMethodToPatch).insertBeforeBody(src);
34 | } else {
35 | ctMethodToPatch.insertBefore(src);
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/patcher/RawPatchInfo.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire.patcher;
2 |
3 | import javassist.CtBehavior;
4 |
5 | import java.lang.reflect.InvocationTargetException;
6 | import java.lang.reflect.Method;
7 |
8 | public class RawPatchInfo extends PatchInfo
9 | {
10 | private Method method;
11 |
12 | public RawPatchInfo(CtBehavior ctMethodToPatch, Method method)
13 | {
14 | super(ctMethodToPatch, null);
15 | this.method = method;
16 | }
17 |
18 | @Override
19 | protected String patchClassName()
20 | {
21 | return method.getDeclaringClass().getName();
22 | }
23 |
24 | @Override
25 | protected String debugMsg()
26 | {
27 | return "Raw Javassist...";
28 | }
29 |
30 | @Override
31 | public int patchOrdering()
32 | {
33 | return 5;
34 | }
35 |
36 | @Override
37 | public void doPatch() throws PatchingException
38 | {
39 | try {
40 | method.invoke(null, ctMethodToPatch);
41 | } catch (IllegalAccessException | InvocationTargetException e) {
42 | throw new PatchingException(e);
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/patcher/ReplacePatchInfo.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire.patcher;
2 |
3 | import javassist.CannotCompileException;
4 | import javassist.CtBehavior;
5 | import javassist.CtMethod;
6 |
7 | public class ReplacePatchInfo extends PatchInfo
8 | {
9 | public ReplacePatchInfo(CtBehavior ctMethodToPatch, CtMethod replaceMethod)
10 | {
11 | super(ctMethodToPatch, replaceMethod);
12 | }
13 |
14 | @Override
15 | protected String debugMsg()
16 | {
17 | return "Replacing...";
18 | }
19 |
20 | @Override
21 | public int patchOrdering()
22 | {
23 | return 0;
24 | }
25 |
26 | @Override
27 | public void doPatch() throws PatchingException
28 | {
29 | try {
30 | ((CtMethod) ctMethodToPatch).setBody(patchMethod, null);
31 | } catch (CannotCompileException e) {
32 | throw new PatchingException(e);
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/patcher/javassist/MyCodeConverter.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire.patcher.javassist;
2 |
3 | import com.evacipated.cardcrawl.modthespire.lib.SpireOverride;
4 | import com.evacipated.cardcrawl.modthespire.patcher.javassist.convert.TransformSpecialCallVirtual;
5 | import javassist.CannotCompileException;
6 | import javassist.CodeConverter;
7 | import javassist.CtMethod;
8 | import javassist.Modifier;
9 |
10 | import java.util.HashSet;
11 | import java.util.Set;
12 |
13 | public class MyCodeConverter extends CodeConverter
14 | {
15 | private static Set done = new HashSet<>();
16 |
17 | public static void reset()
18 | {
19 | done.clear();
20 | }
21 |
22 | public void redirectSpecialMethodCall(CtMethod origMethod) throws CannotCompileException
23 | {
24 | int mod1 = origMethod.getModifiers();
25 | if ((
26 | Modifier.isStatic(mod1)
27 | || !Modifier.isPrivate(mod1)
28 | || origMethod.getDeclaringClass().isInterface()
29 | )
30 | && !done.contains(origMethod.getLongName())
31 | ) {
32 | if (!Modifier.isPrivate(mod1) && !origMethod.hasAnnotation(SpireOverride.class)) {
33 | throw new CannotCompileException("invoke-type not special/private " + origMethod.getLongName());
34 | }
35 | }
36 |
37 | origMethod.setModifiers(Modifier.setProtected(origMethod.getModifiers()));
38 | done.add(origMethod.getLongName());
39 |
40 | transformers = new TransformSpecialCallVirtual(transformers, origMethod);
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/patcher/javassist/convert/TransformSpecialCallVirtual.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire.patcher.javassist.convert;
2 |
3 | import com.evacipated.cardcrawl.modthespire.Loader;
4 | import javassist.ClassPool;
5 | import javassist.CtClass;
6 | import javassist.CtMethod;
7 | import javassist.NotFoundException;
8 | import javassist.bytecode.*;
9 | import javassist.convert.Transformer;
10 |
11 | public class TransformSpecialCallVirtual extends Transformer
12 | {
13 | protected String classname, methodname, methodDescriptor;
14 | protected CodeAttribute codeAttr;
15 |
16 | /* cache */
17 | protected int newIndex;
18 | protected ConstPool constPool;
19 |
20 | public TransformSpecialCallVirtual(Transformer next, CtMethod origMethod)
21 | {
22 | super(next);
23 | methodname = origMethod.getName();
24 | methodDescriptor = origMethod.getMethodInfo2().getDescriptor();
25 | classname = origMethod.getDeclaringClass().getName();
26 | constPool = null;
27 | }
28 |
29 | @Override
30 | public void initialize(ConstPool cp, CodeAttribute attr) {
31 | if (constPool != cp) {
32 | newIndex = 0;
33 | }
34 | codeAttr = attr;
35 | }
36 |
37 | /**
38 | * Modify INVOKESPECIAL to use INVOKEVIRTUAL.
39 | */
40 | @Override
41 | public int transform(CtClass clazz, int pos, CodeIterator iterator,
42 | ConstPool cp) throws BadBytecode
43 | {
44 | int c = iterator.byteAt(pos);
45 | if (c == INVOKESPECIAL) {
46 | int index = iterator.u16bitAt(pos + 1);
47 | String cname = cp.eqMember(methodname, methodDescriptor, index);
48 | if (cname != null && matchClass(cname, clazz.getClassPool())) {
49 | int ntinfo = cp.getMemberNameAndType(index);
50 | pos = match(c, pos, iterator, cp.getNameAndTypeDescriptor(ntinfo), cp);
51 | }
52 | }
53 |
54 | return pos;
55 | }
56 |
57 | private boolean matchClass(String name, ClassPool pool) {
58 | if (classname.equals(name))
59 | return true;
60 |
61 | try {
62 | CtClass clazz = pool.get(name);
63 | CtClass declClazz = pool.get(classname);
64 | if (clazz.subtypeOf(declClazz))
65 | try {
66 | CtMethod m = clazz.getMethod(methodname, methodDescriptor);
67 | return m.getDeclaringClass().getName().equals(classname);
68 | }
69 | catch (NotFoundException e) {
70 | // maybe the original method has been removed.
71 | return true;
72 | }
73 | }
74 | catch (NotFoundException e) {
75 | return false;
76 | }
77 |
78 | return false;
79 | }
80 |
81 | protected int match(int c, int pos, CodeIterator iterator,
82 | int typedesc, ConstPool cp) throws BadBytecode
83 | {
84 | LineNumberAttribute ainfo = (LineNumberAttribute) codeAttr.getAttribute(LineNumberAttribute.tag);
85 |
86 | int nt = cp.addNameAndTypeInfo(cp.addUtf8Info(methodname), typedesc);
87 | int ci = cp.addClassInfo(classname);
88 | if (c == INVOKEINTERFACE) {
89 | newIndex = cp.addInterfaceMethodrefInfo(ci, nt);
90 | } else {
91 | if (c == INVOKESPECIAL) {
92 | if (Loader.DEBUG) {
93 | System.out.println(" @ " + ainfo.toLineNumber(pos));
94 | }
95 | iterator.writeByte(INVOKEVIRTUAL, pos);
96 | }
97 |
98 | newIndex = cp.addMethodrefInfo(ci, nt);
99 | }
100 |
101 | constPool = cp;
102 |
103 | iterator.write16bit(newIndex, pos + 1);
104 | return pos;
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/patches/AllowWatcherUnlock.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire.patches;
2 |
3 | import com.evacipated.cardcrawl.modthespire.lib.SpirePatch2;
4 | import com.evacipated.cardcrawl.modthespire.lib.SpireReturn;
5 | import com.megacrit.cardcrawl.characters.AbstractPlayer;
6 | import com.megacrit.cardcrawl.core.CardCrawlGame;
7 | import com.megacrit.cardcrawl.screens.DeathScreen;
8 | import com.megacrit.cardcrawl.screens.stats.CharStat;
9 | import com.megacrit.cardcrawl.unlock.UnlockTracker;
10 | import com.megacrit.cardcrawl.unlock.misc.DefectUnlock;
11 | import com.megacrit.cardcrawl.unlock.misc.WatcherUnlock;
12 |
13 | import java.util.ArrayList;
14 | import java.util.List;
15 |
16 | @SpirePatch2(
17 | clz=DeathScreen.class,
18 | method="willWatcherUnlock"
19 | )
20 | public class AllowWatcherUnlock
21 | {
22 | public static SpireReturn Prefix(boolean ___defectUnlockedThisRun)
23 | {
24 | if (___defectUnlockedThisRun || !UnlockTracker.isCharacterLocked(WatcherUnlock.KEY)) {
25 | return SpireReturn.Return(false);
26 | }
27 | List baseCharacters = new ArrayList<>();
28 | for (AbstractPlayer c : CardCrawlGame.characterManager.getAllCharacters()) {
29 | switch (c.chosenClass) {
30 | case IRONCLAD:
31 | case THE_SILENT:
32 | case DEFECT:
33 | baseCharacters.add(c.getCharStat());
34 | break;
35 | }
36 | }
37 | return SpireReturn.Return(
38 | !UnlockTracker.isCharacterLocked(DefectUnlock.KEY)
39 | && baseCharacters.stream().anyMatch(stats -> stats.getVictoryCount() > 0)
40 | );
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/patches/AlwaysEnableCustomMode.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire.patches;
2 |
3 | import com.evacipated.cardcrawl.modthespire.lib.SpirePatch;
4 | import com.megacrit.cardcrawl.screens.mainMenu.MenuPanelScreen;
5 | import javassist.CannotCompileException;
6 | import javassist.expr.ExprEditor;
7 | import javassist.expr.FieldAccess;
8 |
9 | @SpirePatch(
10 | clz=MenuPanelScreen.class,
11 | method="initializePanels"
12 | )
13 | public class AlwaysEnableCustomMode
14 | {
15 | public static ExprEditor Instrument()
16 | {
17 | return new ExprEditor() {
18 | @Override
19 | public void edit(FieldAccess f) throws CannotCompileException
20 | {
21 | if (f.getClassName().equals("com.megacrit.cardcrawl.screens.stats.CharStat")
22 | && f.getFieldName().equals("highestDaily")) {
23 | f.replace("$_ = 1;");
24 | }
25 | }
26 | };
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/patches/CatchCrash.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire.patches;
2 |
3 | import com.evacipated.cardcrawl.modthespire.lib.SpirePatch2;
4 | import com.megacrit.cardcrawl.core.ExceptionHandler;
5 |
6 | public class CatchCrash
7 | {
8 | @SpirePatch2(
9 | clz=ExceptionHandler.class,
10 | method="handleException"
11 | )
12 | public static class MegaCritHandler
13 | {
14 | public static void Prefix(Exception e)
15 | {
16 | HandleCrash.crash = e;
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/patches/ChangeWindowTitle.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire.patches;
2 |
3 | import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration;
4 | import com.evacipated.cardcrawl.modthespire.lib.SpirePatch;
5 | import com.megacrit.cardcrawl.desktop.DesktopLauncher;
6 | import javassist.CannotCompileException;
7 | import javassist.expr.ExprEditor;
8 | import javassist.expr.FieldAccess;
9 |
10 | @SpirePatch(
11 | clz=DesktopLauncher.class,
12 | method="main"
13 | )
14 | public class ChangeWindowTitle
15 | {
16 | public static ExprEditor Instrument()
17 | {
18 | return new ExprEditor() {
19 | @Override
20 | public void edit(FieldAccess f) throws CannotCompileException
21 | {
22 | if (f.isWriter() && f.getClassName().equals(LwjglApplicationConfiguration.class.getName())
23 | && f.getFieldName().equals("title")) {
24 | f.replace("$proceed(\"Modded \" + $1);");
25 | }
26 | }
27 | };
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/patches/CreditsModList.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire.patches;
2 |
3 | import com.evacipated.cardcrawl.modthespire.Loader;
4 | import com.evacipated.cardcrawl.modthespire.ModInfo;
5 | import com.evacipated.cardcrawl.modthespire.ReflectionHelper;
6 | import com.evacipated.cardcrawl.modthespire.lib.ByRef;
7 | import com.evacipated.cardcrawl.modthespire.lib.SpireInsertPatch;
8 | import com.evacipated.cardcrawl.modthespire.lib.SpirePatch;
9 | import com.megacrit.cardcrawl.core.Settings;
10 | import com.megacrit.cardcrawl.credits.CreditLine;
11 | import com.megacrit.cardcrawl.credits.CreditsScreen;
12 |
13 | import java.lang.reflect.Field;
14 | import java.util.ArrayList;
15 |
16 | @SpirePatch(
17 | clz=CreditsScreen.class,
18 | method=SpirePatch.CONSTRUCTOR
19 | )
20 | public class CreditsModList {
21 | private static String[] MTS_AUTHORS = new String[] {
22 | "kiooeht",
23 | "t-larson",
24 | "test447"
25 | };
26 |
27 | @SpireInsertPatch(
28 | rloc=5,
29 | localvars={"tmpY"}
30 | )
31 | public static void Insert(CreditsScreen __instance, @ByRef float[] tmpY)
32 | {
33 | try {
34 | Field f = CreditsScreen.class.getDeclaredField("lines");
35 | f.setAccessible(true);
36 | ArrayList lines = (ArrayList)f.get(__instance);
37 |
38 | // ModTheSpire
39 | lines.add(new CreditLine("ModTheSpire", tmpY[0] -= 150.0F, true));
40 | // ModTheSpire authors
41 | for (String author : MTS_AUTHORS) {
42 | lines.add(new CreditLine(author, tmpY[0] -= 45.0F, false));
43 | }
44 | // Loaded mods
45 | for (ModInfo info : Loader.MODINFOS) {
46 | lines.add(new CreditLine(info.Name, tmpY[0] -= 150.0F, true));
47 | for (String author : info.Authors) {
48 | lines.add(new CreditLine(author, tmpY[0] -= 45.0F, false));
49 | }
50 | }
51 | } catch (NoSuchFieldException | IllegalAccessException e) {
52 | e.printStackTrace();
53 | }
54 | }
55 |
56 | public static void Postfix(CreditsScreen __instance)
57 | {
58 | try {
59 | Field f = CreditsScreen.class.getDeclaredField("lines");
60 | f.setAccessible(true);
61 | ArrayList lines = (ArrayList) f.get(__instance);
62 |
63 | int headers = 0;
64 | for (CreditLine line : lines) {
65 | Field color = CreditLine.class.getDeclaredField("color");
66 | color.setAccessible(true);
67 | if (color.get(line).equals(Settings.GOLD_COLOR)) {
68 | ++headers;
69 | }
70 | }
71 |
72 | float NEW_END_OF_CREDITS_Y = 85.0F + (headers * 150.0F) + ((lines.size() - headers) * 45.0F);
73 | NEW_END_OF_CREDITS_Y *= Settings.scale;
74 | Field END_OF_CREDITS_Y = CreditsScreen.class.getDeclaredField("END_OF_CREDITS_Y");
75 | ReflectionHelper.setStaticFinalField(END_OF_CREDITS_Y, NEW_END_OF_CREDITS_Y);
76 | } catch (Exception e) {
77 | e.printStackTrace();
78 | }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/patches/HandleCrash.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire.patches;
2 |
3 | import com.evacipated.cardcrawl.modthespire.Loader;
4 |
5 | import java.net.URL;
6 | import java.util.Arrays;
7 | import java.util.HashSet;
8 | import java.util.Set;
9 | import java.util.stream.Collectors;
10 |
11 | public class HandleCrash
12 | {
13 | public static Throwable crash = null;
14 |
15 | public static void maybeExit()
16 | {
17 | if (crash != null) {
18 | System.err.println("Game crashed.");
19 | Loader.printMTSInfo(System.err);
20 | tryPrintModsInStacktrace(crash);
21 | System.err.println("Cause:");
22 | crash.printStackTrace();
23 | Loader.restoreWindowOnCrash();
24 | } else {
25 | System.out.println("Game closed.");
26 | if (!Loader.DEBUG) {
27 | Loader.closeWindow();
28 | }
29 | }
30 | }
31 |
32 | private static void tryPrintModsInStacktrace(Throwable exception) {
33 | try {
34 | printModsInStacktrace(exception);
35 | } catch (Exception e) {
36 | // ignore
37 | }
38 | }
39 |
40 | private static void printModsInStacktrace(Throwable exception) {
41 | Set classes = new HashSet<>();
42 | addClassesInThrowable(exception, classes);
43 |
44 | Set urls = new HashSet<>();
45 | for (String className : classes) {
46 | try {
47 | Class> cls = Class.forName(className);
48 | urls.add(cls.getProtectionDomain().getCodeSource().getLocation());
49 | } catch (ClassNotFoundException | NoClassDefFoundError ignore) {
50 | // ignore
51 | }
52 | }
53 |
54 | Set modInfoLines = new HashSet<>();
55 | for (URL url : urls) {
56 | if (url == null) {
57 | continue;
58 | }
59 |
60 | Arrays.stream(Loader.MODINFOS).filter(m -> m.jarURL.equals(url)).findFirst().ifPresent(modInfo -> {
61 | if (!modInfo.ID.equals("basemod")) {
62 | modInfoLines.add(String.format("%s (%s)", modInfo.ID, modInfo.ModVersion));
63 | }
64 | });
65 | }
66 |
67 | if (modInfoLines.size() > 0) {
68 | System.err.println("Mods in stacktrace:");
69 | for (String modId : modInfoLines.stream().sorted().collect(Collectors.toList())) {
70 | System.err.println(" - " + modId);
71 | }
72 | }
73 | }
74 |
75 | private static void addClassesInThrowable(Throwable exception, Set classes) {
76 | for (StackTraceElement stackTraceElement : exception.getStackTrace()) {
77 | classes.add(stackTraceElement.getClassName());
78 | }
79 |
80 | for (Throwable suppressed : exception.getSuppressed()) {
81 | if (suppressed != null) {
82 | addClassesInThrowable(suppressed, classes);
83 | }
84 | }
85 |
86 | Throwable cause = exception.getCause();
87 | if (cause != null) {
88 | addClassesInThrowable(cause, classes);
89 | }
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/patches/HeapSize.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire.patches;
2 |
3 | import com.badlogic.gdx.graphics.Color;
4 | import com.badlogic.gdx.graphics.g2d.SpriteBatch;
5 | import com.evacipated.cardcrawl.modthespire.lib.SpireInsertPatch;
6 | import com.evacipated.cardcrawl.modthespire.lib.SpirePatch2;
7 | import com.megacrit.cardcrawl.core.CardCrawlGame;
8 | import com.megacrit.cardcrawl.core.Settings;
9 | import com.megacrit.cardcrawl.helpers.FontHelper;
10 |
11 | @SpirePatch2(
12 | clz = CardCrawlGame.class,
13 | method = "render"
14 | )
15 | public class HeapSize
16 | {
17 | @SpireInsertPatch(
18 | loc = 450
19 | )
20 | public static void Insert(SpriteBatch ___sb)
21 | {
22 | // for testing heap usage, change to true
23 | if (false) {
24 | Runtime runtime = Runtime.getRuntime();
25 | long heapSize = runtime.totalMemory() / 1024;
26 | long heapMaxSize = runtime.maxMemory();
27 | long heapFreeSize = runtime.freeMemory();
28 |
29 | FontHelper.renderFontLeftTopAligned(
30 | ___sb,
31 | FontHelper.tipBodyFont,
32 | "Heap size: " + formatSize(heapSize) + "\n" +
33 | "Max Heap size: " + formatSize(heapMaxSize) + "\n" +
34 | "Free size: " + formatSize(heapFreeSize),
35 | 10,
36 | Settings.HEIGHT - 130,
37 | Color.WHITE
38 | );
39 | }
40 | }
41 |
42 | public static String formatSize(long v) {
43 | if (v < 1024) return v + " B";
44 | int z = (63 - Long.numberOfLeadingZeros(v)) / 10;
45 | return String.format("%.1f %sB", (double)v / (1L << (z*10)), " KMGTPE".charAt(z));
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/patches/IsModded.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire.patches;
2 |
3 | import com.evacipated.cardcrawl.modthespire.lib.SpirePatch;
4 | import com.megacrit.cardcrawl.core.Settings;
5 | import javassist.CannotCompileException;
6 | import javassist.expr.ExprEditor;
7 | import javassist.expr.FieldAccess;
8 |
9 | @SpirePatch(
10 | clz=Settings.class,
11 | method=SpirePatch.STATICINITIALIZER
12 | )
13 | public class IsModded
14 | {
15 | public static ExprEditor Instrument()
16 | {
17 | return new ExprEditor() {
18 | @Override
19 | public void edit(FieldAccess f) throws CannotCompileException
20 | {
21 | if (f.isWriter() && f.getFieldName().equals("isModded")) {
22 | f.replace("$proceed(true);");
23 | } else if (f.isWriter() && f.getFieldName().equals("isDev")) {
24 | f.replace("$proceed(false);");
25 | }
26 | }
27 | };
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/patches/MainMenuModList.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire.patches;
2 |
3 | import com.badlogic.gdx.graphics.g2d.BitmapFont;
4 | import com.evacipated.cardcrawl.modthespire.Loader;
5 | import com.evacipated.cardcrawl.modthespire.lib.SpirePatch;
6 | import com.megacrit.cardcrawl.core.CardCrawlGame;
7 | import com.megacrit.cardcrawl.helpers.FontHelper;
8 | import com.megacrit.cardcrawl.screens.mainMenu.MainMenuScreen;
9 | import javassist.CannotCompileException;
10 | import javassist.expr.ExprEditor;
11 | import javassist.expr.FieldAccess;
12 | import javassist.expr.MethodCall;
13 |
14 | import java.util.regex.Matcher;
15 | import java.util.regex.Pattern;
16 |
17 | @SpirePatch(
18 | clz=MainMenuScreen.class,
19 | method="render"
20 | )
21 | public class MainMenuModList
22 | {
23 | public static ExprEditor Instrument()
24 | {
25 | return new ExprEditor() {
26 | @Override
27 | public void edit(FieldAccess f) throws CannotCompileException
28 | {
29 | if (f.getFieldName().equals("VERSION_INFO")) {
30 | f.replace(String.format("$_ = %s.alterVersion($proceed($$));", MainMenuModList.class.getName()));
31 | }
32 | }
33 |
34 | @Override
35 | public void edit(MethodCall m) throws CannotCompileException
36 | {
37 | if (m.getMethodName().equals("renderSmartText")) {
38 | m.replace(
39 | "{" +
40 | String.format("$5 = %s.getSmartHeight($2, $3, $6, $7);", MainMenuModList.class.getName()) +
41 | "$proceed($$);" +
42 | "}"
43 | );
44 | }
45 | }
46 | };
47 | }
48 |
49 | private static Pattern re = Pattern.compile("(\\[.+] \\(.+\\)(?:v\\d)*) \\[(ModTheSpire .+)]( BETA)?");
50 |
51 | public static String alterVersion(String version)
52 | {
53 | Matcher m = re.matcher(version);
54 | if (!m.find()) {
55 | return version;
56 | }
57 |
58 | String ver = m.group(1);
59 | String mtsver = m.group(2);
60 | String beta = m.group(3);
61 | return ver + (beta == null ? "" : beta) + " NL " + makeMTSVersionModCount(mtsver);
62 | }
63 |
64 | public static String alterVersion2(String version)
65 | {
66 | Matcher m = re.matcher(version);
67 | if (!m.find()) {
68 | return version;
69 | }
70 |
71 | String ver = m.group(1);
72 | String beta = m.group(3);
73 | return ver + (beta == null ? "" : beta);
74 | }
75 |
76 | public static String makeMTSVersionModCount(String version)
77 | {
78 | return version + " - " + Loader.MODINFOS.length + " mod" + (Loader.MODINFOS.length > 1 ? "s" : "");
79 | }
80 |
81 | public static float getSmartHeight(BitmapFont font, String msg, float lineWidth, float lineSpacing)
82 | {
83 | return -FontHelper.getSmartHeight(font, msg, lineWidth, lineSpacing) + lineSpacing;
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/patches/SkipIntro.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire.patches;
2 |
3 | import com.evacipated.cardcrawl.modthespire.Loader;
4 | import com.evacipated.cardcrawl.modthespire.lib.SpirePatch;
5 | import com.evacipated.cardcrawl.modthespire.lib.SpireReturn;
6 | import com.megacrit.cardcrawl.screens.splash.SplashScreen;
7 |
8 | @SpirePatch(
9 | clz=SplashScreen.class,
10 | method="update"
11 | )
12 | public class SkipIntro
13 | {
14 | public static SpireReturn Prefix(SplashScreen __instance)
15 | {
16 | if (Loader.SKIP_INTRO) {
17 | __instance.isDone = true;
18 | return SpireReturn.Return();
19 | }
20 | return SpireReturn.Continue();
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/patches/lwjgl2/CatchCrash.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire.patches.lwjgl2;
2 |
3 | import com.evacipated.cardcrawl.modthespire.lib.SpirePatch;
4 | import com.evacipated.cardcrawl.modthespire.patches.HandleCrash;
5 | import javassist.CannotCompileException;
6 | import javassist.expr.ExprEditor;
7 | import javassist.expr.MethodCall;
8 |
9 | @SpirePatch(
10 | cls="com.badlogic.gdx.backends.lwjgl.LwjglApplication$1",
11 | method="run"
12 | )
13 | public class CatchCrash
14 | {
15 | public static ExprEditor Instrument()
16 | {
17 | return new ExprEditor()
18 | {
19 | @Override
20 | public void edit(MethodCall m) throws CannotCompileException
21 | {
22 | if (m.getMethodName().equals("setCursorCatched")) {
23 | m.replace(
24 | HandleCrash.class.getName() + ".crash = t;" +
25 | "$_ = $proceed($$);"
26 | );
27 | }
28 | }
29 | };
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/patches/lwjgl2/Lwjgl2DisableGdxForceExit.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire.patches.lwjgl2;
2 |
3 | import com.badlogic.gdx.backends.lwjgl.LwjglApplication;
4 | import com.evacipated.cardcrawl.modthespire.lib.SpirePatch;
5 | import com.evacipated.cardcrawl.modthespire.patches.HandleCrash;
6 | import javassist.CannotCompileException;
7 | import javassist.expr.ExprEditor;
8 | import javassist.expr.FieldAccess;
9 |
10 | @SpirePatch(
11 | clz= LwjglApplication.class,
12 | method="mainLoop"
13 | )
14 | public class Lwjgl2DisableGdxForceExit
15 | {
16 | public static ExprEditor Instrument()
17 | {
18 | return new ExprEditor() {
19 | @Override
20 | public void edit(FieldAccess f) throws CannotCompileException
21 | {
22 | if (f.isReader() && f.getFieldName().equals("forceExit")) {
23 | f.replace(
24 | HandleCrash.class.getName() + ".maybeExit();" +
25 | "$_ = false;"
26 | );
27 | }
28 | }
29 | };
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/patches/lwjgl3/CatchCrash.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire.patches.lwjgl3;
2 |
3 | import com.badlogic.gdx.backends.lwjgl3.Lwjgl3Application;
4 | import com.evacipated.cardcrawl.modthespire.lib.SpirePatch;
5 | import com.evacipated.cardcrawl.modthespire.patches.HandleCrash;
6 | import javassist.CannotCompileException;
7 | import javassist.NotFoundException;
8 | import javassist.expr.ExprEditor;
9 | import javassist.expr.Instanceof;
10 |
11 | @SpirePatch(
12 | clz=Lwjgl3Application.class,
13 | method=SpirePatch.CONSTRUCTOR
14 | )
15 | public class CatchCrash
16 | {
17 | public static ExprEditor Instrument()
18 | {
19 | return new ExprEditor()
20 | {
21 | @Override
22 | public void edit(Instanceof i) throws CannotCompileException
23 | {
24 | try {
25 | if (i.getType().getName().equals(RuntimeException.class.getName())) {
26 | i.replace(
27 | HandleCrash.class.getName() + ".crash = $1;" +
28 | "$_ = $proceed($$);"
29 | );
30 | }
31 | } catch (NotFoundException e) {
32 | throw new CannotCompileException(e);
33 | }
34 | }
35 | };
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/patches/lwjgl3/CloseMTSWindow.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire.patches.lwjgl3;
2 |
3 | import com.badlogic.gdx.backends.lwjgl3.Lwjgl3Application;
4 | import com.evacipated.cardcrawl.modthespire.lib.SpirePatch2;
5 | import com.evacipated.cardcrawl.modthespire.patches.HandleCrash;
6 |
7 | @SpirePatch2(
8 | clz = Lwjgl3Application.class,
9 | method = "cleanup"
10 | )
11 | public class CloseMTSWindow
12 | {
13 | public static void Postfix()
14 | {
15 | HandleCrash.maybeExit();
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/patches/lwjgl3/FixController.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire.patches.lwjgl3;
2 |
3 | import com.evacipated.cardcrawl.modthespire.lib.SpirePatch2;
4 | import com.evacipated.cardcrawl.modthespire.lib.SpireRawPatch;
5 | import com.megacrit.cardcrawl.helpers.controller.CInputHelper;
6 | import javassist.*;
7 | import javassist.bytecode.CodeAttribute;
8 | import javassist.bytecode.CodeIterator;
9 | import javassist.bytecode.ConstPool;
10 | import javassist.convert.Transformer;
11 |
12 | // In addition to this patch, gdx-controllers-lwjgl3 has to be shaded
13 | @SpirePatch2(
14 | clz = CInputHelper.class,
15 | method = "initializeIfAble"
16 | )
17 | public class FixController
18 | {
19 | // We do it this way because a normal instrument won't work because the Display class
20 | // reference we're trying to remove gets ignored by the instrument
21 | @SpireRawPatch
22 | public static void RemoveDisplayReference(CtBehavior ctBehavior) throws CannotCompileException
23 | {
24 | ctBehavior.instrument(new MyCodeConverter());
25 | }
26 |
27 | private static class MyCodeConverter extends CodeConverter
28 | {
29 | public MyCodeConverter()
30 | {
31 | transformers = new TransformDisplayIsActive(transformers);
32 | }
33 | }
34 |
35 | private static class TransformDisplayIsActive extends Transformer
36 | {
37 | private static final String className = "org.lwjgl.opengl.Display";
38 | private static final String methodName = "isActive";
39 | private static final String methodDescriptor = "()Z";
40 |
41 |
42 | /* cache */
43 | protected CodeAttribute codeAttr;
44 | protected int newIndex;
45 | protected ConstPool constPool;
46 |
47 | public TransformDisplayIsActive(Transformer next)
48 | {
49 | super(next);
50 | }
51 |
52 | @Override
53 | public void initialize(ConstPool cp, CodeAttribute attr) {
54 | if (constPool != cp) {
55 | newIndex = 0;
56 | }
57 | codeAttr = attr;
58 | }
59 |
60 | @Override
61 | public int transform(CtClass clazz, int pos, CodeIterator it, ConstPool cp)
62 | {
63 | int c = it.byteAt(pos);
64 | if (c == INVOKESTATIC) {
65 | int index = it.u16bitAt(pos + 1);
66 | String cname = cp.eqMember(methodName, methodDescriptor, index);
67 | if (cname != null && matchClass(cname, clazz.getClassPool())) {
68 | it.writeByte(ICONST_1, pos);
69 | it.write16bit(NOP, pos + 1);
70 | }
71 | }
72 |
73 | return pos;
74 | }
75 |
76 | private boolean matchClass(String name, ClassPool pool) {
77 | if (className.equals(name))
78 | return true;
79 |
80 | try {
81 | CtClass clazz = pool.get(name);
82 | CtClass declClazz = pool.get(className);
83 | if (clazz.subtypeOf(declClazz))
84 | try {
85 | CtMethod m = clazz.getMethod(methodName, methodDescriptor);
86 | return m.getDeclaringClass().getName().equals(className);
87 | }
88 | catch (NotFoundException e) {
89 | // maybe the original method has been removed.
90 | return true;
91 | }
92 | }
93 | catch (NotFoundException e) {
94 | return false;
95 | }
96 |
97 | return false;
98 | }
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/patches/lwjgl3/FramerateLimiter.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire.patches.lwjgl3;
2 |
3 | import com.badlogic.gdx.backends.lwjgl3.Lwjgl3Application;
4 | import com.evacipated.cardcrawl.modthespire.lib.*;
5 | import com.megacrit.cardcrawl.characters.AbstractPlayer;
6 | import javassist.CtBehavior;
7 |
8 | @SpirePatch2(
9 | clz = Lwjgl3Application.class,
10 | method = "loop"
11 | )
12 | public class FramerateLimiter
13 | {
14 | public static int fps = 60;
15 | private static final Sync sync = new Sync();
16 |
17 | @SpireInsertPatch(
18 | loc = 180
19 | )
20 | public static void Insert()
21 | {
22 | sync.sync(fps);
23 | }
24 |
25 | private static class Locator extends SpireInsertLocator
26 | {
27 | @Override
28 | public int[] Locate(CtBehavior ctBehavior) throws Exception
29 | {
30 | Matcher matcher = new Matcher.FieldAccessMatcher(AbstractPlayer.class, "powers");
31 | return LineFinder.findInOrder(ctBehavior, matcher);
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/patches/modsscreen/BaseMod/DisableBaseModBadges.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire.patches.modsscreen.BaseMod;
2 |
3 | import com.evacipated.cardcrawl.modthespire.lib.SpirePatch;
4 |
5 | import java.lang.reflect.Field;
6 | import java.util.ArrayList;
7 |
8 | @SpirePatch(
9 | cls="basemod.BaseMod",
10 | method="publishPostInitialize",
11 | optional=true
12 | )
13 | public class DisableBaseModBadges
14 | {
15 | public static void Postfix()
16 | {
17 | try {
18 | Class> basemod = DisableBaseModBadges.class.getClassLoader().loadClass("basemod.BaseMod");
19 | Field f = basemod.getDeclaredField("renderSubscribers");
20 | f.setAccessible(true);
21 | ArrayList> renderSubscribers = (ArrayList>)f.get(null);
22 | renderSubscribers.removeIf(o -> o.getClass().getName().equals("basemod.ModBadge"));
23 |
24 | f = basemod.getDeclaredField("preUpdateSubscribers");
25 | f.setAccessible(true);
26 | ArrayList> preUpdateSubscribers = (ArrayList>)f.get(null);
27 | preUpdateSubscribers.removeIf(o -> o.getClass().getName().equals("basemod.ModBadge"));
28 | } catch (ClassNotFoundException e) {
29 | e.printStackTrace();
30 | } catch (NoSuchFieldException e) {
31 | e.printStackTrace();
32 | } catch (IllegalAccessException e) {
33 | e.printStackTrace();
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/patches/modsscreen/BaseMod/ModBadgeOnClick.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire.patches.modsscreen.BaseMod;
2 |
3 | import com.evacipated.cardcrawl.modthespire.lib.SpirePatch;
4 | import javassist.CannotCompileException;
5 | import javassist.CtBehavior;
6 |
7 | @SpirePatch(
8 | cls="basemod.ModBadge",
9 | method="onClick",
10 | optional=true
11 | )
12 | public class ModBadgeOnClick
13 | {
14 | public static void Raw(CtBehavior ctMethodToPatch)
15 | {
16 | try {
17 | ctMethodToPatch.setBody(
18 | "if (modPanel != null) {" +
19 | "modPanel.oldInputProcessor = com.badlogic.gdx.Gdx.input.getInputProcessor();" +
20 | "basemod.BaseMod.modSettingsUp = true;" +
21 | "modPanel.isUp = true;" +
22 | "modPanel.onCreate();" +
23 | "}"
24 | );
25 | } catch (CannotCompileException e) {
26 | e.printStackTrace();
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/patches/modsscreen/MainMenuItem.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire.patches.modsscreen;
2 |
3 | import com.evacipated.cardcrawl.modthespire.lib.*;
4 | import com.evacipated.cardcrawl.modthespire.patcher.PatchingException;
5 | import com.megacrit.cardcrawl.screens.compendium.CardLibraryScreen;
6 | import com.megacrit.cardcrawl.screens.mainMenu.MainMenuScreen;
7 | import com.megacrit.cardcrawl.screens.mainMenu.MenuButton;
8 | import javassist.CannotCompileException;
9 | import javassist.CtBehavior;
10 |
11 | import java.util.Arrays;
12 |
13 | @SpirePatch(
14 | clz=MainMenuScreen.class,
15 | method="setMainMenuButtons"
16 | )
17 | public class MainMenuItem
18 | {
19 | @SpireInsertPatch(
20 | locator=Locator.class,
21 | localvars={"index"}
22 | )
23 | public static void Insert(Object __obj_instance, @ByRef int[] index)
24 | {
25 | MainMenuScreen __instance = (MainMenuScreen)__obj_instance;
26 | __instance.buttons.add(new MenuButton(ModMenuButton.MODS, index[0]++));
27 | }
28 |
29 | private static class Locator extends SpireInsertLocator
30 | {
31 | @Override
32 | public int[] Locate(CtBehavior ctMethodToPatch) throws CannotCompileException, PatchingException
33 | {
34 | Matcher finalMatcher = new Matcher.FieldAccessMatcher(MenuButton.ClickResult.class, "PATCH_NOTES");
35 | return LineFinder.findInOrder(ctMethodToPatch, finalMatcher);
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/patches/modsscreen/ModMenuButton.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire.patches.modsscreen;
2 |
3 | import com.evacipated.cardcrawl.modthespire.lib.SpirePatch;
4 | import com.evacipated.cardcrawl.modthespire.lib.SpireEnum;
5 | import com.megacrit.cardcrawl.screens.mainMenu.MenuButton;
6 |
7 | import java.lang.reflect.Field;
8 |
9 | public class ModMenuButton
10 | {
11 | @SpireEnum
12 | static MenuButton.ClickResult MODS;
13 |
14 | static ModsScreen modsScreen = null;
15 |
16 | @SpirePatch(
17 | clz=MenuButton.class,
18 | method="setLabel"
19 | )
20 | public static class SetLabel
21 | {
22 | public static void Postfix(MenuButton __instance)
23 | {
24 | try {
25 | if (__instance.result == MODS) {
26 | Field f_label = MenuButton.class.getDeclaredField("label");
27 | f_label.setAccessible(true);
28 | f_label.set(__instance, "Mods");
29 | }
30 | } catch (Exception e) {
31 | e.printStackTrace();
32 | }
33 | }
34 | }
35 |
36 | @SpirePatch(
37 | clz=MenuButton.class,
38 | method="buttonEffect"
39 | )
40 | public static class ButtonEffect
41 | {
42 | public static void Postfix(MenuButton __instance)
43 | {
44 | if (__instance.result == MODS) {
45 | if (modsScreen == null) {
46 | modsScreen = new ModsScreen();
47 | }
48 | modsScreen.open();
49 | }
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/patches/modsscreen/ModsScreenUpdateRender.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire.patches.modsscreen;
2 |
3 | import com.badlogic.gdx.graphics.g2d.SpriteBatch;
4 | import com.evacipated.cardcrawl.modthespire.lib.SpirePatch;
5 | import com.megacrit.cardcrawl.screens.mainMenu.MainMenuScreen;
6 |
7 | public class ModsScreenUpdateRender
8 | {
9 | @SpirePatch(
10 | clz=MainMenuScreen.class,
11 | method="update"
12 | )
13 | public static class Update
14 | {
15 | public static void Postfix(MainMenuScreen __instance)
16 | {
17 | if (__instance.screen == ModsScreen.Enum.MODS_LIST) {
18 | ModMenuButton.modsScreen.update();
19 | }
20 | }
21 | }
22 |
23 | @SpirePatch(
24 | clz=MainMenuScreen.class,
25 | method="render"
26 | )
27 | public static class Render
28 | {
29 | public static void Postfix(MainMenuScreen __instance, SpriteBatch sb)
30 | {
31 | if (__instance.screen == ModsScreen.Enum.MODS_LIST) {
32 | ModMenuButton.modsScreen.render(sb);
33 | }
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/patches/modsscreen/SaveBaseModBadges.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire.patches.modsscreen;
2 |
3 | import com.badlogic.gdx.graphics.Texture;
4 | import com.evacipated.cardcrawl.modthespire.Loader;
5 | import com.evacipated.cardcrawl.modthespire.lib.SpireInsertPatch;
6 | import com.evacipated.cardcrawl.modthespire.lib.SpirePatch;
7 | import javassist.CannotCompileException;
8 | import javassist.ClassPool;
9 | import javassist.CtClass;
10 | import javassist.NotFoundException;
11 | import javassist.expr.ExprEditor;
12 | import javassist.expr.MethodCall;
13 |
14 | import java.net.MalformedURLException;
15 | import java.net.URISyntaxException;
16 | import java.net.URL;
17 | import java.util.HashMap;
18 |
19 | @SpirePatch(
20 | cls="basemod.BaseMod",
21 | method="registerModBadge",
22 | optional=true
23 | )
24 | public class SaveBaseModBadges
25 | {
26 | @SpireInsertPatch(
27 | rloc=8,
28 | localvars={"badge"}
29 | )
30 | public static void Insert(Texture t, String name, String author, String desc, Object settingsPanel, Object badge)
31 | {
32 | if (ModsScreen.baseModBadges == null) {
33 | ModsScreen.baseModBadges = new HashMap<>();
34 | }
35 |
36 | StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace();
37 | try {
38 | Class> cls = SaveBaseModBadges.class.getClassLoader().loadClass(stacktrace[3].getClassName());
39 | URL location = cls.getProtectionDomain().getCodeSource().getLocation();
40 | if (location == null) {
41 | try {
42 | ClassPool pool = Loader.getClassPool();
43 | CtClass ctCls = pool.get(cls.getName());
44 | String url = ctCls.getURL().getFile();
45 | int i = url.lastIndexOf('!');
46 | url = url.substring(0, i);
47 | location = new URL(url);
48 | } catch (NotFoundException | MalformedURLException e) {
49 | e.printStackTrace();
50 | }
51 | }
52 | if (location != null) {
53 | URL modFile = location.toURI().toURL();
54 | ModsScreen.baseModBadges.put(modFile, badge);
55 | }
56 | } catch (ClassNotFoundException | URISyntaxException | MalformedURLException e) {
57 | e.printStackTrace();
58 | }
59 | }
60 |
61 | public static ExprEditor Instrument()
62 | {
63 | return new ExprEditor() {
64 | @Override
65 | public void edit(MethodCall m) throws CannotCompileException
66 | {
67 | if (m.getMethodName().equals("add")) {
68 | m.replace("$_ = true;");
69 | }
70 | }
71 | };
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/ui/TextFieldWithPlaceholder.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire.ui;
2 |
3 | import javax.swing.*;
4 | import java.awt.*;
5 | import java.awt.event.FocusEvent;
6 | import java.awt.event.FocusListener;
7 |
8 | public class TextFieldWithPlaceholder extends JTextField {
9 | private String placeholder;
10 |
11 | public TextFieldWithPlaceholder() {
12 | this.addFocusListener(new FocusListener() {
13 | @Override
14 | public void focusGained(FocusEvent e) {
15 | TextFieldWithPlaceholder.this.repaint();
16 | }
17 |
18 | @Override
19 | public void focusLost(FocusEvent e) {
20 | TextFieldWithPlaceholder.this.repaint();
21 | }
22 | });
23 | }
24 |
25 | @Override
26 | protected void paintComponent(Graphics g) {
27 | super.paintComponent(g);
28 |
29 | if(getText().isEmpty() && FocusManager.getCurrentKeyboardFocusManager().getFocusOwner() != this){
30 | Graphics2D g2 = (Graphics2D)g.create();
31 | g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
32 | g2.setColor(Color.gray);
33 | g2.setFont(getFont().deriveFont(Font.ITALIC));
34 | g2.drawString(placeholder, 5, getHeight() - 10);
35 | g2.dispose();
36 | }
37 | }
38 |
39 | public void setPlaceholder(String placeholder) {
40 | this.placeholder = placeholder;
41 | this.revalidate();
42 | this.repaint();
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/main/java/com/evacipated/cardcrawl/modthespire/ui/UpdateWindow.java:
--------------------------------------------------------------------------------
1 | package com.evacipated.cardcrawl.modthespire.ui;
2 |
3 | import com.evacipated.cardcrawl.modthespire.DownloadAndRestarter;
4 | import com.evacipated.cardcrawl.modthespire.ModUpdate;
5 |
6 | import javax.swing.*;
7 | import javax.swing.border.EmptyBorder;
8 | import java.awt.*;
9 | import java.awt.event.ActionEvent;
10 | import java.io.IOException;
11 | import java.net.URISyntaxException;
12 | import java.net.URL;
13 |
14 | class UpdateWindow extends JDialog
15 | {
16 | UpdateWindow(JFrame parent)
17 | {
18 | super(parent);
19 | setModal(true);
20 | initUI();
21 | }
22 |
23 | private void initUI()
24 | {
25 | if (ModSelectWindow.MODUPDATES.size() == 1) {
26 | setTitle("Update Available");
27 | } else {
28 | setTitle("Updates Available");
29 | }
30 | setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
31 | setResizable(true);
32 |
33 | getContentPane().setPreferredSize(new Dimension(300, 200));
34 |
35 | rootPane.setBorder(new EmptyBorder(5, 5, 5, 5));
36 |
37 | setLayout(new BorderLayout());
38 |
39 | DefaultListModel model = new DefaultListModel<>();
40 | JList list = new JList<>(model);
41 | JScrollPane modScroller = new JScrollPane(list);
42 | getContentPane().add(modScroller, BorderLayout.CENTER);
43 |
44 | for (ModUpdate update : ModSelectWindow.MODUPDATES) {
45 | model.addElement(update.info.Name);
46 | }
47 |
48 | String tmp;
49 | if (ModSelectWindow.MODUPDATES.size() == 1) {
50 | tmp = "The following mod has an update available:";
51 | } else {
52 | tmp = "The following mods have updates available:";
53 | }
54 | getContentPane().add(new JLabel(tmp), BorderLayout.NORTH);
55 |
56 | JPanel btnPanel = new JPanel(new GridBagLayout());
57 | JButton downloadBtn = new JButton("Download Updates and Restart ModTheSpire");
58 | JButton browserBtn = new JButton("Open Releases in Browser");
59 | GridBagConstraints c = new GridBagConstraints();
60 | c.fill = GridBagConstraints.HORIZONTAL;
61 | c.weightx = 0.5;
62 | btnPanel.add(downloadBtn, c);
63 | c.gridy = 1;
64 | btnPanel.add(browserBtn, c);
65 | getContentPane().add(btnPanel, BorderLayout.SOUTH);
66 |
67 | // Open each update's release url in browser
68 | browserBtn.addActionListener((ActionEvent event) -> {
69 | if (Desktop.isDesktopSupported()) {
70 | for (ModUpdate update : ModSelectWindow.MODUPDATES) {
71 | try {
72 | Desktop.getDesktop().browse(update.releaseURL.toURI());
73 | } catch (IOException | URISyntaxException e) {
74 | e.printStackTrace();
75 | }
76 | }
77 | }
78 | });
79 |
80 | // Download each update
81 | downloadBtn.addActionListener((ActionEvent event) -> {
82 | getContentPane().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
83 | URL[] downloadURLs = new URL[ModSelectWindow.MODUPDATES.size()];
84 | for (int i=0; iBill Burke
31 | * @author Shigeru Chiba
32 | */
33 | public abstract class MemberValue {
34 | ConstPool cp;
35 | char tag;
36 |
37 | MemberValue(char tag, ConstPool cp) {
38 | this.cp = cp;
39 | this.tag = tag;
40 | }
41 |
42 | /**
43 | * Returns the value. If the value type is a primitive type, the
44 | * returned value is boxed.
45 | */
46 | abstract Object getValue(ClassLoader cl, ClassPool cp, Method m)
47 | throws ClassNotFoundException;
48 |
49 | abstract Class> getType(ClassLoader cl) throws ClassNotFoundException;
50 |
51 | static Class> loadClass(ClassLoader cl, String classname)
52 | throws ClassNotFoundException, NoSuchClassError
53 | {
54 | try {
55 | //return Class.forName(convertFromArray(classname), true, cl);
56 | return Class.forName(convertFromArray(classname), false, cl);
57 | }
58 | catch (LinkageError e) {
59 | throw new NoSuchClassError(classname, e);
60 | }
61 | }
62 |
63 | private static String convertFromArray(String classname)
64 | {
65 | int index = classname.indexOf("[]");
66 | if (index != -1) {
67 | String rawType = classname.substring(0, index);
68 | StringBuffer sb = new StringBuffer(Descriptor.of(rawType));
69 | while (index != -1) {
70 | sb.insert(0, "[");
71 | index = classname.indexOf("[]", index + 1);
72 | }
73 | return sb.toString().replace('/', '.');
74 | }
75 | return classname;
76 | }
77 |
78 | /**
79 | * Accepts a visitor.
80 | */
81 | public abstract void accept(MemberValueVisitor visitor);
82 |
83 | /**
84 | * Writes the value.
85 | */
86 | public abstract void write(AnnotationsWriter w) throws IOException;
87 | }
88 |
89 |
90 |
--------------------------------------------------------------------------------
/src/main/java/org/clapper/util/classutil/AbstractClassFilter.java:
--------------------------------------------------------------------------------
1 | package org.clapper.util.classutil;
2 |
3 | import java.lang.reflect.Modifier;
4 |
5 | /**
6 | * AbstractClassFilter implements a {@link ClassFilter}
7 | * that matches class names that (a) can be loaded and (b) are abstract. It
8 | * relies on the pool of classes read by a {@link ClassFinder}; it's
9 | * not really useful by itself.
10 | *
11 | * This class is really just a convenient specialization of the
12 | * {@link ClassModifiersClassFilter} class.
13 | *
14 | * @see ClassFilter
15 | * @see ClassModifiersClassFilter
16 | * @see ClassFinder
17 | * @see Modifier
18 | */
19 | public class AbstractClassFilter
20 | extends ClassModifiersClassFilter
21 | {
22 | /*----------------------------------------------------------------------*\
23 | Constructor
24 | \*----------------------------------------------------------------------*/
25 |
26 | /**
27 | * Construct a new AbstractClassFilter that will accept
28 | * only abstract classes.
29 | */
30 | public AbstractClassFilter()
31 | {
32 | super (Modifier.ABSTRACT);
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/main/java/org/clapper/util/classutil/ClassFilter.java:
--------------------------------------------------------------------------------
1 | package org.clapper.util.classutil;
2 |
3 | /**
4 | * Instances of classes that implement this interface are used, with a
5 | * {@link ClassFinder} object, to filter class names. This interface is
6 | * deliberately reminiscent of the java.io.FilenameFilter
7 | * interface.
8 | *
9 | * @see ClassFinder
10 | */
11 | public interface ClassFilter
12 | {
13 | /**
14 | * Tests whether a class name should be included in a class name
15 | * list.
16 | *
17 | * @param classInfo the loaded information about the class
18 | * @param classFinder the {@link ClassFinder} that called this filter
19 | * (mostly for access to ClassFinder
20 | * utility methods)
21 | *
22 | * @return true if and only if the name should be included
23 | * in the list; false otherwise
24 | */
25 | public boolean accept (ClassInfo classInfo, ClassFinder classFinder);
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/org/clapper/util/classutil/ClassModifiersClassFilter.java:
--------------------------------------------------------------------------------
1 | package org.clapper.util.classutil;
2 |
3 | /**
4 | * ClassModifiersClassFilter is a {@link ClassFilter} that
5 | * matches class names that (a) can be loaded and (b) match a set of class
6 | * modifiers (as defined by the constants in the
7 | * java.lang.reflect.Modifier class). For instance, the the
8 | * following code fragment defines a filter that will match only public
9 | * final classes:
10 | *
11 | *
12 | * import java.lang.reflect.Modifier;
13 | *
14 | * ...
15 | *
16 | * ClassFilter = new ClassModifiersClassFilter (Modifier.PUBLIC | Modifier.FINAL);
17 | *
18 | *
19 | * This class relies on the pool of classes read by a
20 | * {@link ClassFinder}; it's not really useful by itself.
21 | *
22 | * @see ClassFilter
23 | * @see ClassFinder
24 | * @see java.lang.reflect.Modifier
25 | */
26 | public class ClassModifiersClassFilter implements ClassFilter
27 | {
28 | /*----------------------------------------------------------------------*\
29 | Private Data Items
30 | \*----------------------------------------------------------------------*/
31 |
32 | private int modifiers = 0;
33 |
34 | /*----------------------------------------------------------------------*\
35 | Constructor
36 | \*----------------------------------------------------------------------*/
37 |
38 | /**
39 | * Construct a new ClassModifiersClassFilter that will accept
40 | * any classes with the specified modifiers.
41 | *
42 | * @param modifiers the bit-field of modifier flags. See the
43 | * java.lang.reflect.Modifier class for
44 | * legal values.
45 | */
46 | public ClassModifiersClassFilter (int modifiers)
47 | {
48 | super();
49 | this.modifiers = modifiers;
50 | }
51 |
52 | /**
53 | * Tests whether a class name should be included in a class name
54 | * list.
55 | *
56 | * @param classInfo the loaded information about the class
57 | * @param classFinder the {@link ClassFinder} that called this filter
58 | * (mostly for access to ClassFinder
59 | * utility methods)
60 | *
61 | * @return true if and only if the name should be included
62 | * in the list; false otherwise
63 | */
64 | public boolean accept (ClassInfo classInfo, ClassFinder classFinder)
65 | {
66 | return ((classInfo.getModifier() & modifiers) != 0);
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/main/java/org/clapper/util/classutil/InterfaceOnlyClassFilter.java:
--------------------------------------------------------------------------------
1 | package org.clapper.util.classutil;
2 |
3 | import java.lang.reflect.Modifier;
4 |
5 | /**
6 | * InterfaceOnlyClassFilter implements a {@link ClassFilter}
7 | * that matches class names that (a) can be loaded and (b) are interfaces. It
8 | * relies on the pool of classes read by a {@link ClassFinder}; it's
9 | * not really useful by itself.
10 | *
11 | * This class is really just a convenient specialization of the
12 | * {@link ClassModifiersClassFilter} class.
13 | */
14 | public class InterfaceOnlyClassFilter
15 | extends ClassModifiersClassFilter
16 | {
17 | /*----------------------------------------------------------------------*\
18 | Constructor
19 | \*----------------------------------------------------------------------*/
20 |
21 | /**
22 | * Construct a new InterfaceOnlyClassFilter that will accept
23 | * only classes that are interfaces.
24 | */
25 | public InterfaceOnlyClassFilter()
26 | {
27 | super (Modifier.INTERFACE);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/main/java/org/clapper/util/classutil/NotClassFilter.java:
--------------------------------------------------------------------------------
1 | package org.clapper.util.classutil;
2 |
3 | /**
4 | * NotClassFilter is a {@link ClassFilter} that
5 | * wraps another {@link ClassFilter} and negates the sense of the
6 | * wrapped filter's {@link ClassFilter#accept accept()} method. This
7 | * class conceptually provides a logical "NOT" operator for class name
8 | * filters. For example, the following code fragment will create a filter
9 | * that finds all classes that are not interfaces.
10 | *
11 | *
12 | * NotClassFilter filter = new NotClassFilter (new InterfaceOnlyClassFilter());
13 | *
14 | *
15 | * @see ClassFilter
16 | * @see AndClassFilter
17 | * @see OrClassFilter
18 | * @see ClassFinder
19 | * @see InterfaceOnlyClassFilter
20 | */
21 | public class NotClassFilter implements ClassFilter
22 | {
23 | /*----------------------------------------------------------------------*\
24 | Private Data Items
25 | \*----------------------------------------------------------------------*/
26 |
27 | private ClassFilter filter;
28 |
29 | /*----------------------------------------------------------------------*\
30 | Constructor
31 | \*----------------------------------------------------------------------*/
32 |
33 | /**
34 | * Create a new NotClassFilter that wraps the
35 | * specified {@link ClassFilter}.
36 | *
37 | * @param filter The {@link ClassFilter} to wrap.
38 | */
39 | public NotClassFilter (ClassFilter filter)
40 | {
41 | this.filter = filter;
42 | }
43 |
44 | /*----------------------------------------------------------------------*\
45 | Public Methods
46 | \*----------------------------------------------------------------------*/
47 |
48 | /**
49 | * Tests whether a class name should be included in a class name
50 | * list.
51 | *
52 | * @param classInfo the {@link ClassInfo} object to test
53 | * @param classFinder the invoking {@link ClassFinder} object
54 | *
55 | * @return true if and only if the name should be included
56 | * in the list; false otherwise
57 | */
58 | public boolean accept (ClassInfo classInfo, ClassFinder classFinder)
59 | {
60 | return ! this.filter.accept (classInfo, classFinder);
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/main/java/org/clapper/util/classutil/RegexClassFilter.java:
--------------------------------------------------------------------------------
1 | package org.clapper.util.classutil;
2 |
3 | import java.util.regex.Pattern;
4 | import java.util.regex.PatternSyntaxException;
5 |
6 | /**
7 | * RegexClassFilter is a {@link ClassFilter} that matches class
8 | * names using a regular expression. Multiple regular expression filters
9 | * can be combined using {@link AndClassFilter} and/or
10 | * {@link OrClassFilter} objects.
11 | *
12 | * This class does not have to load the classes it's filtering; it
13 | * matches on the class name only.
14 | *
15 | * RegexClassFilter uses the java.util.regex
16 | * regular expression classes.
17 | *
18 | * @see ClassFilter
19 | * @see AndClassFilter
20 | * @see OrClassFilter
21 | * @see NotClassFilter
22 | * @see ClassFinder
23 | */
24 | public class RegexClassFilter
25 | implements ClassFilter
26 | {
27 | /*----------------------------------------------------------------------*\
28 | Private Data Items
29 | \*----------------------------------------------------------------------*/
30 |
31 | private Pattern pattern;
32 |
33 | /*----------------------------------------------------------------------*\
34 | Constructor
35 | \*----------------------------------------------------------------------*/
36 |
37 | /**
38 | * Construct a new RegexClassFilter using the specified
39 | * pattern.
40 | *
41 | * @param regex the regular expression to add
42 | *
43 | * @throws PatternSyntaxException bad regular expression
44 | */
45 | public RegexClassFilter (String regex)
46 | throws PatternSyntaxException
47 | {
48 | pattern = Pattern.compile (regex);
49 | }
50 |
51 | /**
52 | * Construct a new RegexClassFilter using the specified
53 | * pattern.
54 | *
55 | * @param regex the regular expression to add
56 | * @param regexFlags regular expression compilation flags (e.g.,
57 | * Pattern.CASE_INSENSITIVE ). See
58 | * the Javadocs for java.util.regex for
59 | * legal values.
60 | *
61 | * @throws PatternSyntaxException bad regular expression
62 | */
63 | public RegexClassFilter (String regex, int regexFlags)
64 | throws PatternSyntaxException
65 | {
66 | pattern = Pattern.compile (regex, regexFlags);
67 | }
68 |
69 | /*----------------------------------------------------------------------*\
70 | Public Methods
71 | \*----------------------------------------------------------------------*/
72 |
73 | /**
74 | * Determine whether a class name is to be accepted or not, based on
75 | * the regular expression specified to the constructor.
76 | *
77 | * @param classInfo the {@link ClassInfo} object to test
78 | * @param classFinder the invoking {@link ClassFinder} object
79 | *
80 | * @return true if the class name matches,
81 | * false if it doesn't
82 | */
83 | public boolean accept (ClassInfo classInfo, ClassFinder classFinder)
84 | {
85 | return pattern.matcher (classInfo.getClassName()).find();
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/src/main/java/org/clapper/util/classutil/SubclassClassFilter.java:
--------------------------------------------------------------------------------
1 | package org.clapper.util.classutil;
2 |
3 | import java.util.Map;
4 | import java.util.HashMap;
5 |
6 | /**
7 | * SubclassClassFilter is a {@link ClassFilter} that matches
8 | * class names that (a) can be loaded and (b) extend a given subclass or
9 | * implement a specified interface, directly or indirectly. It uses the
10 | * java.lang.Class.isAssignableFrom() method, so it actually has to
11 | * load each class it tests. For maximum flexibility, a
12 | * SubclassClassFilter can be configured to use a specific class
13 | * loader.
14 | */
15 | public class SubclassClassFilter implements ClassFilter
16 | {
17 | /*----------------------------------------------------------------------*\
18 | Private Data Items
19 | \*----------------------------------------------------------------------*/
20 |
21 | private Class baseClass;
22 |
23 | /*----------------------------------------------------------------------*\
24 | Constructor
25 | \*----------------------------------------------------------------------*/
26 |
27 | /**
28 | * Construct a new SubclassClassFilter that will accept
29 | * only classes that extend the specified class or implement the
30 | * specified interface.
31 | *
32 | * @param baseClassOrInterface the base class or interface
33 | */
34 | public SubclassClassFilter (Class baseClassOrInterface)
35 | {
36 | this.baseClass = baseClassOrInterface;
37 | }
38 |
39 | /*----------------------------------------------------------------------*\
40 | Public Methods
41 | \*----------------------------------------------------------------------*/
42 |
43 | /**
44 | * Perform the acceptance test on the loaded Class object.
45 | *
46 | * @param classInfo the {@link ClassInfo} object to test
47 | * @param classFinder the invoking {@link ClassFinder} object
48 | *
49 | * @return true if the class name matches,
50 | * false if it doesn't
51 | */
52 | public boolean accept (ClassInfo classInfo, ClassFinder classFinder)
53 | {
54 | Map superClasses = new HashMap();
55 |
56 | if (baseClass.isInterface())
57 | classFinder.findAllInterfaces (classInfo, superClasses);
58 | else
59 | classFinder.findAllSuperClasses (classInfo, superClasses);
60 |
61 | return superClasses.keySet().contains (baseClass.getName());
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/main/java/org/clapper/util/cmdline/OptionComparator.java:
--------------------------------------------------------------------------------
1 | package org.clapper.util.cmdline;
2 |
3 | import java.util.Comparator;
4 |
5 | /**
6 | * Used solely by UsageInfo , an instance of this class compares
7 | * OptionInfo items, by short option name or long option name, in
8 | * a case-insensitive manner.
9 | *
10 | * @see UsageInfo
11 | * @see OptionInfo
12 | */
13 | final class OptionComparator implements Comparator
14 | {
15 | private boolean ignoreCase = false;
16 |
17 | public OptionComparator()
18 | {
19 | // Nothing to do
20 | }
21 |
22 | public OptionComparator (boolean ignoreCase)
23 | {
24 | this.ignoreCase = ignoreCase;
25 | }
26 |
27 | public int compare (OptionInfo o1, OptionInfo o2)
28 | {
29 | String s1 = getComparisonString (o1);
30 | String s2 = getComparisonString (o2);
31 |
32 | return ignoreCase ? s1.compareToIgnoreCase (s2) : s1.compareTo (s2);
33 | }
34 |
35 | public boolean equals (Object o)
36 | {
37 | return (this.getClass().isInstance (o));
38 | }
39 |
40 | public int hashCode() // NOPMD
41 | {
42 | return super.hashCode();
43 | }
44 |
45 | private String getComparisonString (OptionInfo opt)
46 | {
47 | String result = "";
48 |
49 | if (opt.shortOption != UsageInfo.NO_SHORT_OPTION)
50 | result = String.valueOf (opt.shortOption);
51 | else if (opt.longOption != null)
52 | result = opt.longOption;
53 |
54 | return result;
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/main/java/org/clapper/util/cmdline/OptionInfo.java:
--------------------------------------------------------------------------------
1 | package org.clapper.util.cmdline;
2 |
3 | /**
4 | * Used solely by UsageInfo , this class contains information about
5 | * an option.
6 | *
7 | * @see UsageInfo
8 | */
9 | final class OptionInfo
10 | {
11 | char shortOption;
12 | String longOption;
13 | String argToken;
14 | String explanation;
15 |
16 | OptionInfo (char shortOption,
17 | String longOption,
18 | String argToken,
19 | String explanation)
20 | {
21 | this.shortOption = shortOption;
22 | this.longOption = longOption;
23 | this.argToken = argToken;
24 | this.explanation = explanation;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/org/clapper/util/cmdline/Package.java:
--------------------------------------------------------------------------------
1 | package org.clapper.util.cmdline;
2 |
3 | /**
4 | * Package-specific shared utility methods. Used only within this package.
5 | */
6 | final class Package
7 | {
8 | /*----------------------------------------------------------------------*\
9 | Constants
10 | \*----------------------------------------------------------------------*/
11 |
12 | /**
13 | * The name of the resource bundle for this package.
14 | */
15 | static final String BUNDLE_NAME = "org.clapper.util.cmdline.Bundle";
16 |
17 | /*----------------------------------------------------------------------*\
18 | Constructor
19 | \*----------------------------------------------------------------------*/
20 |
21 | private Package()
22 | {
23 | // Can't be built
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/java/org/clapper/util/cmdline/ParameterHandler.java:
--------------------------------------------------------------------------------
1 | package org.clapper.util.cmdline;
2 |
3 | import java.util.Iterator;
4 | import java.util.NoSuchElementException;
5 |
6 | /**
7 | * Thie interface defines the callback methods used by a
8 | * {@link ParameterParser} object, when its
9 | * {@link ParameterParser#parse parse()} method is called.
10 | *
11 | * @see ParameterParser
12 | * @see CommandLineUtility
13 | */
14 | public interface ParameterHandler
15 | {
16 | /*----------------------------------------------------------------------*\
17 | Constants
18 | \*----------------------------------------------------------------------*/
19 |
20 | /*----------------------------------------------------------------------*\
21 | Public Methods
22 | \*----------------------------------------------------------------------*/
23 |
24 | /**
25 | * Handles a parsed option.
26 | *
27 | * @param shortOption the (character) short option, if any; otherwise,
28 | * the constant {@link UsageInfo#NO_SHORT_OPTION}.
29 | * @param longOption the (string) long option, if any; otherwise,
30 | * null.
31 | * @param it An Iterator from which to retrieve any
32 | * value(s) for the option
33 | *
34 | * @throws CommandLineUsageException on error
35 | * @throws NoSuchElementException attempt to iterate past end of args;
36 | * {@link ParameterParser#parse}
37 | * automatically handles this exception,
38 | * so it's safe for implementations of
39 | * this method not to handle it
40 | */
41 | public void parseOption(char shortOption,
42 | String longOption,
43 | Iterator it)
44 | throws CommandLineUsageException,
45 | NoSuchElementException;
46 |
47 | /**
48 | * Handles all parameters that appear after the end of the options. If there
49 | * are no such parameters, the implementation of this method should just
50 | * return without doing anything.
51 | *
52 | * @param it the Iterator containing the parameters
53 | *
54 | * @throws CommandLineUsageException on error
55 | * @throws NoSuchElementException attempt to iterate past end of args;
56 | * {@link ParameterParser#parse}
57 | * automatically handles this exception,
58 | * so it's safe for implementations of
59 | * this method not to handle it
60 | */
61 | public void parsePostOptionParameters(Iterator it)
62 | throws CommandLineUsageException,
63 | NoSuchElementException;
64 | }
65 |
--------------------------------------------------------------------------------
/src/main/java/org/clapper/util/config/EnvSection.java:
--------------------------------------------------------------------------------
1 | package org.clapper.util.config;
2 |
3 | import java.util.HashMap;
4 | import java.util.Map;
5 |
6 | /**
7 | * Implements the special "env" section
8 | *
9 | * @see Section
10 | */
11 | class EnvSection extends Section
12 | {
13 | /*----------------------------------------------------------------------*\
14 | Constructor
15 | \*----------------------------------------------------------------------*/
16 |
17 | /**
18 | * Allocate a new EnvSection object, loading its values from
19 | * the environment.
20 | *
21 | * @param name the section name
22 | * @param id the ID
23 | */
24 | EnvSection (String name, int id)
25 | {
26 | super (name, id);
27 |
28 | // Need a modifiable copy of the environment map, so we can
29 | // escape any embedded backslashes. (That's necessary because the
30 | // values from the environment will be subsituted pre-parse.)
31 |
32 | Map env = new HashMap (System.getenv());
33 | super.addVariables (escapeEmbeddedBackslashes (env));
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/main/java/org/clapper/util/config/NoSuchSectionException.java:
--------------------------------------------------------------------------------
1 | package org.clapper.util.config;
2 |
3 | /**
4 | * A NoSuchSectionException is thrown by the
5 | * {@link Configuration} class to signify that a requested configuration
6 | * section does not exist.
7 | *
8 | * @see ConfigurationException
9 | */
10 | public class NoSuchSectionException extends ConfigurationException
11 | {
12 | /*----------------------------------------------------------------------*\
13 | Private Static Variables
14 | \*----------------------------------------------------------------------*/
15 |
16 | /**
17 | * See JDK 1.5 version of java.io.Serializable
18 | */
19 | private static final long serialVersionUID = 1L;
20 |
21 | /*----------------------------------------------------------------------*\
22 | Private Data Items
23 | \*----------------------------------------------------------------------*/
24 |
25 | private String sectionName = null;
26 |
27 | /*----------------------------------------------------------------------*\
28 | Constructor
29 | \*----------------------------------------------------------------------*/
30 |
31 | /**
32 | * Constructs an exception.
33 | *
34 | * @param sectionName the section name to which the exception pertains
35 | */
36 | public NoSuchSectionException (String sectionName)
37 | {
38 | super (Package.BUNDLE_NAME,
39 | "noSuchSection",
40 | "Configuration section \"{0}\" does not exist",
41 | new Object[] {sectionName});
42 |
43 | this.sectionName = sectionName;
44 | }
45 |
46 | /*----------------------------------------------------------------------*\
47 | Public Methods
48 | \*----------------------------------------------------------------------*/
49 |
50 | /**
51 | * Gets the section name associated with this exception.
52 | *
53 | * @return the section name
54 | */
55 | public String getSectionName()
56 | {
57 | return sectionName;
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/main/java/org/clapper/util/config/NoSuchVariableException.java:
--------------------------------------------------------------------------------
1 | package org.clapper.util.config;
2 |
3 | /**
4 | * A NoSuchVariableException is thrown by the
5 | * {@link Configuration} class to signify that a requested configuration
6 | * variable does not exist.
7 | *
8 | * @see ConfigurationException
9 | */
10 | public class NoSuchVariableException extends ConfigurationException
11 | {
12 | /*----------------------------------------------------------------------*\
13 | Private Static Variables
14 | \*----------------------------------------------------------------------*/
15 |
16 | /**
17 | * See JDK 1.5 version of java.io.Serializable
18 | */
19 | private static final long serialVersionUID = 1L;
20 |
21 | /*----------------------------------------------------------------------*\
22 | Private Data Items
23 | \*----------------------------------------------------------------------*/
24 |
25 | private String variableName = null;
26 | private String sectionName = null;
27 |
28 | /*----------------------------------------------------------------------*\
29 | Constructor
30 | \*----------------------------------------------------------------------*/
31 |
32 | /**
33 | * Constructs an exception.
34 | *
35 | * @param sectionName the section that doesn't have the variable
36 | * @param variableName the variable name to which the exception pertains
37 | */
38 | public NoSuchVariableException (String sectionName, String variableName)
39 | {
40 | super (Package.BUNDLE_NAME,
41 | "noSuchVariable",
42 | "Variable \"{0}\" does not exist in configuration section " +
43 | "\"{1}\"",
44 | new Object[] {variableName, sectionName});
45 |
46 | this.sectionName = sectionName;
47 | this.variableName = variableName;
48 | }
49 |
50 | /*----------------------------------------------------------------------*\
51 | Public Methods
52 | \*----------------------------------------------------------------------*/
53 |
54 | /**
55 | * Gets the section name associated with this exception.
56 | *
57 | * @return the section name
58 | */
59 | public String getSectionName()
60 | {
61 | return sectionName;
62 | }
63 |
64 | /**
65 | * Gets the variable name associated with this exception.
66 | *
67 | * @return the variable name
68 | */
69 | public String getVariableName()
70 | {
71 | return variableName;
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/src/main/java/org/clapper/util/config/Package.java:
--------------------------------------------------------------------------------
1 | package org.clapper.util.config;
2 |
3 | /**
4 | * Package-specific shared utility methods. Used only within this package.
5 | */
6 | final class Package
7 | {
8 | /*----------------------------------------------------------------------*\
9 | Constants
10 | \*----------------------------------------------------------------------*/
11 |
12 | /**
13 | * The name of the resource bundle for this package.
14 | */
15 | static final String BUNDLE_NAME = "org.clapper.util.config.Bundle";
16 |
17 | /*----------------------------------------------------------------------*\
18 | Constructor
19 | \*----------------------------------------------------------------------*/
20 |
21 | private Package()
22 | {
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/main/java/org/clapper/util/config/SectionExistsException.java:
--------------------------------------------------------------------------------
1 | package org.clapper.util.config;
2 |
3 | /**
4 | * A SectionExistsException is thrown by the
5 | * {@link Configuration} class to signify that a requested configuration
6 | * section already exists and cannot be created.
7 | *
8 | * @see ConfigurationException
9 | */
10 | public class SectionExistsException extends ConfigurationException
11 | {
12 | /*----------------------------------------------------------------------*\
13 | Private Static Variables
14 | \*----------------------------------------------------------------------*/
15 |
16 | /**
17 | * See JDK 1.5 version of java.io.Serializable
18 | */
19 | private static final long serialVersionUID = 1L;
20 |
21 | /*----------------------------------------------------------------------*\
22 | Private Data Items
23 | \*----------------------------------------------------------------------*/
24 |
25 | private String sectionName = null;
26 |
27 | /*----------------------------------------------------------------------*\
28 | Constructor
29 | \*----------------------------------------------------------------------*/
30 |
31 | /**
32 | * Constructs an exception.
33 | *
34 | * @param sectionName the section name to which the exception pertains
35 | */
36 | public SectionExistsException (String sectionName)
37 | {
38 | super (NoSuchSectionException.class.getName() +
39 | ": section " +
40 | sectionName);
41 |
42 | this.sectionName = sectionName;
43 | }
44 |
45 | /*----------------------------------------------------------------------*\
46 | Public Methods
47 | \*----------------------------------------------------------------------*/
48 |
49 | /**
50 | * Gets the section name associated with this exception.
51 | *
52 | * @return the section name
53 | */
54 | public String getSectionName()
55 | {
56 | return sectionName;
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/main/java/org/clapper/util/config/SystemSection.java:
--------------------------------------------------------------------------------
1 | package org.clapper.util.config;
2 |
3 | import java.util.Map;
4 | import org.clapper.util.misc.PropertiesMap;
5 |
6 | /**
7 | * Implements the special "system" section
8 | *
9 | * @see Section
10 | */
11 | class SystemSection extends Section
12 | {
13 | /*----------------------------------------------------------------------*\
14 | Constructor
15 | \*----------------------------------------------------------------------*/
16 |
17 | /**
18 | * Allocate a new SystemSection object, loading its values from
19 | * the system properties list.
20 | *
21 | * @param name the section name
22 | * @param id the ID
23 | */
24 | SystemSection (String name, int id)
25 | {
26 | super (name, id);
27 |
28 | // Escape any embedded backslashes in variable values.
29 |
30 | Map propMap = new PropertiesMap (System.getProperties());
31 | super.addVariables (escapeEmbeddedBackslashes (propMap));
32 | }
33 |
34 | /*----------------------------------------------------------------------*\
35 | Package-visible Methods
36 | \*----------------------------------------------------------------------*/
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/src/main/java/org/clapper/util/config/ValueSegment.java:
--------------------------------------------------------------------------------
1 | package org.clapper.util.config;
2 |
3 | import org.clapper.util.text.XStringBuilder;
4 |
5 | /**
6 | * A variable value segment. This is just a substring, with a flag that
7 | * indicates whether the segment is literal, white space-escaped or not.
8 | */
9 | class ValueSegment
10 | {
11 | XStringBuilder segmentBuf = new XStringBuilder();
12 | boolean isLiteral = false;
13 | boolean isWhiteSpaceEscaped = false;
14 |
15 | ValueSegment()
16 | {
17 | // Nothing to do
18 | }
19 |
20 | void append (char ch)
21 | {
22 | segmentBuf.append (ch);
23 | }
24 |
25 | int length()
26 | {
27 | return segmentBuf.length();
28 | }
29 |
30 | public String toString()
31 | {
32 | return segmentBuf.toString();
33 | }
34 |
35 | public ValueSegment makeCopy()
36 | {
37 | ValueSegment copy = new ValueSegment();
38 | copy.segmentBuf = new XStringBuilder(this.segmentBuf.toString());
39 | copy.isLiteral = this.isLiteral;
40 | copy.isWhiteSpaceEscaped = this.isWhiteSpaceEscaped;
41 | return copy;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/main/java/org/clapper/util/config/package.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | org.clapper.util.config - A Configuration File Parser.
7 | This package contains classes to parse an extended .INI-style
8 | configuration file.
9 |
10 |
11 |
--------------------------------------------------------------------------------
/src/main/java/org/clapper/util/io/AndFileFilter.java:
--------------------------------------------------------------------------------
1 | package org.clapper.util.io;
2 |
3 | import java.util.LinkedList;
4 | import java.util.List;
5 |
6 | import java.io.FileFilter;
7 | import java.io.File;
8 |
9 | /**
10 | * An AndFileFilter contains logically ANDs other
11 | * java.io.FileFilter objects. When its {@link #accept accept()}
12 | * method is called, the AndFileFilter object passes the file
13 | * through the contained filters. The file is only accepted if it is
14 | * accepted by all contained filters. This class conceptually provides a
15 | * logical "AND" operator for file filters.
16 | *
17 | * The contained filters are applied in the order they were added to the
18 | * AndFileFilter object. This class's {@link #accept accept()}
19 | * method stops looping over the contained filters as soon as it encounters
20 | * one whose accept() method returns false (implementing
21 | * a "short-circuited AND" operation.)
22 | *
23 | * @see FileFilter
24 | * @see OrFileFilter
25 | * @see NotFileFilter
26 | * @see AndFilenameFilter
27 | * @see RegexFileFilter
28 | */
29 | public final class AndFileFilter implements FileFilter
30 | {
31 | /*----------------------------------------------------------------------*\
32 | Private Data Items
33 | \*----------------------------------------------------------------------*/
34 |
35 | private List filters = new LinkedList();
36 |
37 | /*----------------------------------------------------------------------*\
38 | Constructor
39 | \*----------------------------------------------------------------------*/
40 |
41 | /**
42 | * Construct a new AndFileFilter with no contained filters.
43 | */
44 | public AndFileFilter()
45 | {
46 | // nothing to do
47 | }
48 |
49 | /**
50 | * Construct a new AndFileFilter with a set of contained filters.
51 | * Additional filters may be added later, via calls to the
52 | * {@link #addFilter addFilter()} method.
53 | *
54 | * @param filters filters to add
55 | */
56 | public AndFileFilter (FileFilter... filters)
57 | {
58 | for (FileFilter filter : filters)
59 | addFilter (filter);
60 | }
61 |
62 | /*----------------------------------------------------------------------*\
63 | Public Methods
64 | \*----------------------------------------------------------------------*/
65 |
66 | /**
67 | * Add a filter to the set of contained filters.
68 | *
69 | * @param filter the FileFilter to add.
70 | *
71 | * @return this object, to permit chained calls.
72 | *
73 | * @see #removeFilter
74 | */
75 | public AndFileFilter addFilter (FileFilter filter)
76 | {
77 | filters.add (filter);
78 | return this;
79 | }
80 |
81 | /**
82 | * Remove a filter from the set of contained filters.
83 | *
84 | * @param filter the FileFilter to remove.
85 | *
86 | * @see #addFilter
87 | */
88 | public void removeFilter (FileFilter filter)
89 | {
90 | filters.remove (filter);
91 | }
92 |
93 | /**
94 | * Determine whether a file is to be accepted or not, based on the
95 | * contained filters. The file is accepted if any one of the contained
96 | * filters accepts it. This method stops looping over the contained
97 | * filters as soon as it encounters one whose accept() method
98 | * returns false (implementing a "short-circuited AND"
99 | * operation.)
100 | *
101 | * If the set of contained filters is empty, then this method
102 | * returns true .
103 | *
104 | * @param file The file to check for acceptance
105 | *
106 | * @return true if the file matches, false if it doesn't
107 | */
108 | public boolean accept (File file)
109 | {
110 | boolean accepted = true;
111 |
112 | for (FileFilter filter : filters)
113 | {
114 | accepted = filter.accept (file);
115 | if (! accepted)
116 | break;
117 | }
118 |
119 | return accepted;
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/src/main/java/org/clapper/util/io/AndFilenameFilter.java:
--------------------------------------------------------------------------------
1 | package org.clapper.util.io;
2 |
3 | import java.util.Collection;
4 | import java.util.LinkedList;
5 | import java.util.List;
6 | import java.util.Iterator;
7 |
8 | import java.io.FilenameFilter;
9 | import java.io.File;
10 |
11 | /**
12 | * An AndFilenameFilter logically ANDs other
13 | * java.io.FilenameFilter objects. When its
14 | * {@link #accept accept()} method is called, the AndFilenameFilter
15 | * object passes the file through the contained filters. The file is only
16 | * accepted if it is accepted by all contained filters. This class
17 | * conceptually provides a logical "AND" operator for file filters.
18 | *
19 | * The contained filters are applied in the order they were added to the
20 | * AndFilenameFilter object. This class's {@link #accept accept()}
21 | * method stops looping over the contained filters as soon as it encounters
22 | * one whose accept() method returns false (implementing
23 | * a "short-circuited AND" operation.)
24 | *
25 | * @see FilenameFilter
26 | * @see OrFilenameFilter
27 | * @see NotFilenameFilter
28 | * @see RegexFilenameFilter
29 | * @see AndFileFilter
30 | */
31 | public final class AndFilenameFilter implements FilenameFilter
32 | {
33 | /*----------------------------------------------------------------------*\
34 | Private Data Items
35 | \*----------------------------------------------------------------------*/
36 |
37 | private List filters = new LinkedList();
38 |
39 | /*----------------------------------------------------------------------*\
40 | Constructor
41 | \*----------------------------------------------------------------------*/
42 |
43 | /**
44 | * Construct a new AndFilenameFilter with no contained filters.
45 | */
46 | public AndFilenameFilter()
47 | {
48 | // nothing to do
49 | }
50 |
51 | /**
52 | * Construct a new AndFilenameFilter with a set of contained
53 | * filters. Additional filters may be added later, via calls to the
54 | * {@link #addFilter addFilter()} method.
55 | *
56 | * @param filters filters to use
57 | */
58 | public AndFilenameFilter (FilenameFilter... filters)
59 | {
60 | for (FilenameFilter filter : filters)
61 | addFilter (filter);
62 | }
63 |
64 | /*----------------------------------------------------------------------*\
65 | Public Methods
66 | \*----------------------------------------------------------------------*/
67 |
68 | /**
69 | * Add a filter to the set of contained filters.
70 | *
71 | * @param filter the FilenameFilter to add.
72 | *
73 | * @return this object, to permit chained calls.
74 | *
75 | * @see #removeFilter
76 | */
77 | public AndFilenameFilter addFilter (FilenameFilter filter)
78 | {
79 | filters.add (filter);
80 | return this;
81 | }
82 |
83 | /**
84 | * Remove a filter from the set of contained filters.
85 | *
86 | * @param filter the FilenameFilter to remove.
87 | *
88 | * @see #addFilter
89 | */
90 | public void removeFilter (FilenameFilter filter)
91 | {
92 | filters.remove (filter);
93 | }
94 |
95 | /**
96 | * Determine whether a file is to be accepted or not, based on the
97 | * contained filters. The file is accepted if any one of the contained
98 | * filters accepts it. This method stops looping over the contained
99 | * filters as soon as it encounters one whose accept() method
100 | * returns false (implementing a "short-circuited AND"
101 | * operation.)
102 | *
103 | * If the set of contained filters is empty, then this method
104 | * returns true .
105 | *
106 | * @param dir The directory containing the file.
107 | * @param name the file name
108 | *
109 | * @return true if the file matches, false if it doesn't
110 | */
111 | public boolean accept (File dir, String name)
112 | {
113 | boolean accepted = true;
114 |
115 | for (FilenameFilter filter : filters)
116 | {
117 | accepted = filter.accept (dir, name);
118 | if (! accepted)
119 | break;
120 | }
121 |
122 | return accepted;
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/src/main/java/org/clapper/util/io/CombinationFilterMode.java:
--------------------------------------------------------------------------------
1 | package org.clapper.util.io;
2 |
3 | /**
4 | * Used solely to define type-safe mode values for
5 | * {@link CombinationFilenameFilter} and {@link CombinationFileFilter}.
6 | *
7 | * @see CombinationFileFilter
8 | * @see CombinationFilenameFilter
9 | */
10 | public enum CombinationFilterMode
11 | {
12 | /**
13 | * Mode setting that instructs the filter to AND all the
14 | * contained filters.
15 | */
16 | AND_FILTERS,
17 |
18 | /**
19 | * Mode setting that instructs the filter to OR all the
20 | * contained filters.
21 | */
22 | OR_FILTERS
23 | };
24 |
--------------------------------------------------------------------------------
/src/main/java/org/clapper/util/io/DirectoryFilter.java:
--------------------------------------------------------------------------------
1 | package org.clapper.util.io;
2 |
3 | import java.io.*;
4 |
5 | /**
6 | * DirectoryFilter implements a
7 | * java.io.FileFilter that matches only directories.
8 | */
9 | public class DirectoryFilter implements FileFilter
10 | {
11 | /*----------------------------------------------------------------------*\
12 | Constructor
13 | \*----------------------------------------------------------------------*/
14 |
15 | public DirectoryFilter()
16 | {
17 | // Nothing to do
18 | }
19 |
20 | /*----------------------------------------------------------------------*\
21 | Public Methods
22 | \*----------------------------------------------------------------------*/
23 |
24 | /**
25 | * Determine whether the specified file is a directory or not.
26 | *
27 | * @return true if the file is a directory, false if not
28 | */
29 | public boolean accept (File f)
30 | {
31 | return f.isDirectory();
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/main/java/org/clapper/util/io/FileFilterMatchType.java:
--------------------------------------------------------------------------------
1 | package org.clapper.util.io;
2 |
3 | /**
4 | *
The FileFilterMatchType enumeration spells out the legal
5 | * match types for {@link RegexFilenameFilter} and {@link RegexFileFilter}.
6 | *
7 | * @see RegexFileFilter
8 | * @see RegexFilenameFilter
9 | */
10 | public enum FileFilterMatchType
11 | {
12 | FILENAME, PATH
13 | }
14 |
--------------------------------------------------------------------------------
/src/main/java/org/clapper/util/io/FileOnlyFilter.java:
--------------------------------------------------------------------------------
1 | package org.clapper.util.io;
2 |
3 | import java.io.*;
4 |
5 | /**
6 | * FileOnlyFilter implements a
7 | * java.io.FileFilter that matches anything that isn't a directory.
8 | * Note that the following two lines produce semantically equivalent filters:
9 | *
10 | *
11 | * FileFilter f1 = new FileOnlyFilter();
12 | * FileFilter f1 = new NotFileFilter (new DirectoryFilter());
13 | *
14 | */
15 | public class FileOnlyFilter implements FileFilter
16 | {
17 | /*----------------------------------------------------------------------*\
18 | Constructor
19 | \*----------------------------------------------------------------------*/
20 |
21 | public FileOnlyFilter()
22 | {
23 | // Nothing to do
24 | }
25 |
26 | /*----------------------------------------------------------------------*\
27 | Public Methods
28 | \*----------------------------------------------------------------------*/
29 |
30 | /**
31 | * Determine whether the specified file is a directory or not.
32 | *
33 | * @return true if the file is a directory, false if not
34 | */
35 | public boolean accept (File f)
36 | {
37 | return f.isFile();
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/main/java/org/clapper/util/io/JustifyStyle.java:
--------------------------------------------------------------------------------
1 | package org.clapper.util.io;
2 |
3 | /**
4 | * The JustifyStyle enumeration spells out the legal field
5 | * justification values for classes such as {@link JustifyTextWriter}. It
6 | * resides in a separate class for readability.
7 | *
8 | * @see JustifyTextWriter
9 | * @see org.clapper.util.text.TextUtil#rightJustifyString(String,int)
10 | * @see org.clapper.util.text.TextUtil#leftJustifyString(String,int)
11 | * @see org.clapper.util.text.TextUtil#centerString(String,int)
12 | */
13 | public enum JustifyStyle
14 | {
15 | RIGHT_JUSTIFY, LEFT_JUSTIFY, CENTER
16 | }
17 |
--------------------------------------------------------------------------------
/src/main/java/org/clapper/util/io/NotFileFilter.java:
--------------------------------------------------------------------------------
1 | package org.clapper.util.io;
2 |
3 | import java.io.FileFilter;
4 | import java.io.File;
5 |
6 | /**
7 | * NotFileFilter is a FileFilter that wraps another
8 | * FileFilter and negates the sense of the wrapped filter's
9 | * accept() method. This class conceptually provides a logical
10 | * "NOT" operator for file filters. For example, the following code
11 | * fragment will create a filter that finds all files that are not
12 | * directories.
13 | *
14 | *
15 | * NotFileFilter filter = new NotFileFilter (new DirectoryFilter());
16 | *
17 | *
18 | * @see FileFilter
19 | * @see AndFileFilter
20 | * @see OrFileFilter
21 | * @see DirectoryFilter
22 | */
23 | public class NotFileFilter implements FileFilter
24 | {
25 | /*----------------------------------------------------------------------*\
26 | Private Data Items
27 | \*----------------------------------------------------------------------*/
28 |
29 | private FileFilter filter;
30 |
31 | /*----------------------------------------------------------------------*\
32 | Constructor
33 | \*----------------------------------------------------------------------*/
34 |
35 | /**
36 | * Create a new NotFileFilter that wraps the
37 | * specified {@link FileFilter}.
38 | *
39 | * @param filter The {@link FileFilter} to wrap.
40 | */
41 | public NotFileFilter (FileFilter filter)
42 | {
43 | this.filter = filter;
44 | }
45 |
46 | /*----------------------------------------------------------------------*\
47 | Constructor
48 | \*----------------------------------------------------------------------*/
49 |
50 | /**
51 | * Tests whether a file should be included in a file list.
52 | *
53 | * @param file The file to check for acceptance
54 | *
55 | * @return true if the file matches, false if it doesn't
56 | */
57 | public boolean accept (File file)
58 | {
59 | return ! this.filter.accept (file);
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/main/java/org/clapper/util/io/NotFilenameFilter.java:
--------------------------------------------------------------------------------
1 | package org.clapper.util.io;
2 |
3 | import java.io.FilenameFilter;
4 | import java.io.File;
5 |
6 | /**
7 | * NotFilenameFilter is a FilenameFilter that wraps another
8 | * FilenameFilter and negates the sense of the wrapped filter's
9 | * accept() method. This class conceptually provides a logical
10 | * "NOT" operator for file filters. For example, the following code
11 | * fragment will create a filter that finds all files that do not start with
12 | * the letter "A".
13 | *
14 | *
15 | * NotFilenameFilter filter = new NotFilenameFilter (new RegexFilenameFilter ("^[Aa]", FileFilterMatchType.NAME));
16 | *
17 | *
18 | * @see FilenameFilter
19 | * @see AndFilenameFilter
20 | * @see OrFilenameFilter
21 | */
22 | public class NotFilenameFilter implements FilenameFilter
23 | {
24 | /*----------------------------------------------------------------------*\
25 | Private Data Items
26 | \*----------------------------------------------------------------------*/
27 |
28 | private FilenameFilter filter;
29 |
30 | /*----------------------------------------------------------------------*\
31 | Constructor
32 | \*----------------------------------------------------------------------*/
33 |
34 | /**
35 | * Create a new NotFilenameFilter that wraps the
36 | * specified {@link FilenameFilter}.
37 | *
38 | * @param filter The {@link FilenameFilter} to wrap.
39 | */
40 | public NotFilenameFilter (FilenameFilter filter)
41 | {
42 | this.filter = filter;
43 | }
44 |
45 | /*----------------------------------------------------------------------*\
46 | Constructor
47 | \*----------------------------------------------------------------------*/
48 |
49 | /**
50 | * Tests whether a file should be included in a file list.
51 | *
52 | * @param dir The directory containing the file.
53 | * @param name the file name
54 | *
55 | * @return true if the file matches, false if it doesn't
56 | */
57 | public boolean accept (File dir, String name)
58 | {
59 | return ! this.filter.accept (dir, name);
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/main/java/org/clapper/util/io/OrFileFilter.java:
--------------------------------------------------------------------------------
1 | package org.clapper.util.io;
2 |
3 | import java.util.Collection;
4 | import java.util.LinkedList;
5 | import java.util.List;
6 | import java.util.Iterator;
7 |
8 | import java.io.FileFilter;
9 | import java.io.File;
10 |
11 | /**
12 | * An OrFileFilter logically ORs other java.io.FileFilter
13 | * objects. When its {@link #accept accept()} method is called, the
14 | * OrFileFilter object passes the file through the contained
15 | * filters. The file is accepted if it is accepted by any of the contained
16 | * filters. This class conceptually provides a logical "OR" operator for
17 | * file filters.
18 | *
19 | * The contained filters are applied in the order they were added to the
20 | * OrFileFilter object. This class's {@link #accept accept()}
21 | * method stops looping over the contained filters as soon as it encounters
22 | * one whose accept() method returns true (implementing
23 | * a "short-circuited OR" operation.)
24 | *
25 | * @see FileFilter
26 | * @see AndFileFilter
27 | * @see NotFileFilter
28 | * @see OrFilenameFilter
29 | * @see RegexFileFilter
30 | */
31 | public final class OrFileFilter implements FileFilter
32 | {
33 | /*----------------------------------------------------------------------*\
34 | Private Data Items
35 | \*----------------------------------------------------------------------*/
36 |
37 | private List filters = new LinkedList();
38 |
39 | /*----------------------------------------------------------------------*\
40 | Constructor
41 | \*----------------------------------------------------------------------*/
42 |
43 | /**
44 | * Construct a new OrFileFilter with no contained filters.
45 | */
46 | public OrFileFilter()
47 | {
48 | // Nothing to do
49 | }
50 |
51 | /**
52 | * Construct a new OrFileFilter with two contained filters.
53 | * Additional filters may be added later, via calls to the
54 | * {@link #addFilter addFilter()} method.
55 | *
56 | * @param filters filters to add
57 | */
58 | public OrFileFilter (FileFilter... filters)
59 | {
60 | for (FileFilter filter : filters)
61 | addFilter (filter);
62 | }
63 |
64 | /*----------------------------------------------------------------------*\
65 | Public Methods
66 | \*----------------------------------------------------------------------*/
67 |
68 | /**
69 | * Add a filter to the set of contained filters.
70 | *
71 | * @param filter the FileFilter to add.
72 | *
73 | * @return this object, to permit chained calls.
74 | *
75 | * @see #removeFilter
76 | */
77 | public OrFileFilter addFilter (FileFilter filter)
78 | {
79 | filters.add (filter);
80 | return this;
81 | }
82 |
83 | /**
84 | * Remove a filter from the set of contained filters.
85 | *
86 | * @param filter the FileFilter to remove.
87 | *
88 | * @see #addFilter
89 | */
90 | public void removeFilter (FileFilter filter)
91 | {
92 | filters.remove (filter);
93 | }
94 |
95 | /**
96 | * Determine whether a file is to be accepted or not, based on the
97 | * contained filters. The file is accepted if any one of the contained
98 | * filters accepts it. This method stops looping over the contained
99 | * filters as soon as it encounters one whose accept() method
100 | * returns false (implementing a "short-circuited AND"
101 | * operation.)
102 | *
103 | * If the set of contained filters is empty, then this method
104 | * returns true .
105 | *
106 | * @param file The file to check for acceptance
107 | *
108 | * @return true if the file matches, false if it doesn't
109 | */
110 | public boolean accept (File file)
111 | {
112 | boolean accepted = false;
113 |
114 | if (filters.size() == 0)
115 | accepted = true;
116 |
117 | else
118 | {
119 | for (FileFilter filter : filters)
120 | {
121 | accepted = filter.accept (file);
122 | if (accepted)
123 | break;
124 | }
125 | }
126 |
127 | return accepted;
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/src/main/java/org/clapper/util/io/Package.java:
--------------------------------------------------------------------------------
1 | package org.clapper.util.io;
2 |
3 | /**
4 | * Package-specific shared utility methods. Used only within this package.
5 | */
6 | final class Package
7 | {
8 | /*----------------------------------------------------------------------*\
9 | Constants
10 | \*----------------------------------------------------------------------*/
11 |
12 | /**
13 | * The name of the resource bundle for this package.
14 | */
15 | static final String BUNDLE_NAME = "org.clapper.util.io.Bundle";
16 |
17 | /*----------------------------------------------------------------------*\
18 | Constructor
19 | \*----------------------------------------------------------------------*/
20 |
21 | private Package()
22 | {
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/main/java/org/clapper/util/io/RecursiveFileFinder.java:
--------------------------------------------------------------------------------
1 | package org.clapper.util.io;
2 |
3 | import java.io.File;
4 | import java.io.FileFilter;
5 | import java.io.FilenameFilter;
6 |
7 | import java.util.Collection;
8 |
9 | /**
10 | * A RecursiveFileFinder walks a directory tree and finds all
11 | * files (or directories) that satisfy caller-supplied criteria. The
12 | * match criteria are specified via either a FileFilter or
13 | * FilenameFilter object.
14 | */
15 | public class RecursiveFileFinder
16 | {
17 | /*----------------------------------------------------------------------*\
18 | Constructors
19 | \*----------------------------------------------------------------------*/
20 |
21 | /**
22 | * Construct a new RecursiveFileFinder object. The various
23 | * find() methods actually provide the searching capabilities.
24 | */
25 | public RecursiveFileFinder()
26 | {
27 | }
28 |
29 | /*----------------------------------------------------------------------*\
30 | Public Methods
31 | \*----------------------------------------------------------------------*/
32 |
33 | /**
34 | * Find all files beneath a given directory. This version of
35 | * find() takes no filter, so every file and directory is
36 | * matched.
37 | *
38 | * @param directory the starting directory
39 | * @param collection where to store the found File objects
40 | *
41 | * @return the number of File objects found
42 | */
43 | public int findFiles (File directory, Collection collection)
44 | {
45 | return findFiles (directory, (FileFilter) null, collection);
46 | }
47 |
48 | /**
49 | * Find all files beneath a given directory, filtered by the specified
50 | * FilenameFilter .
51 | *
52 | * @param directory the starting directory
53 | * @param filter the FilenameFilter to use to filter the
54 | * file names, or null to accept all files
55 | * @param collection where to store the found File objects
56 | *
57 | * @return the number of File objects found
58 | */
59 | public int findFiles (File directory,
60 | FilenameFilter filter,
61 | Collection collection)
62 | {
63 | int total = 0;
64 | File[] files = directory.listFiles (filter);
65 |
66 | if (files != null)
67 | {
68 | for (int i = 0; i < files.length; i++)
69 | collection.add (files[i]);
70 |
71 | total = files.length;
72 | }
73 |
74 | File[] dirs = directory.listFiles (new DirectoryFilter());
75 | if (dirs != null)
76 | {
77 | for (int i = 0; i < dirs.length; i++)
78 | total += findFiles (dirs[i], filter, collection);
79 | }
80 |
81 | return total;
82 | }
83 |
84 | /**
85 | * Find all files beneath a given directory, filtered by the specified
86 | * FilenameFilter .
87 | *
88 | * @param directory the starting directory
89 | * @param filter the FilenameFilter to use to filter the
90 | * file names, or null to accept all files
91 | * @param collection where to store the found File objects
92 | *
93 | * @return the number of File objects found
94 | */
95 | public int findFiles (File directory,
96 | FileFilter filter,
97 | Collection collection)
98 | {
99 | int total = 0;
100 | File[] files = directory.listFiles (filter);
101 |
102 | if (files != null)
103 | {
104 | for (int i = 0; i < files.length; i++)
105 | collection.add (files[i]);
106 |
107 | total = files.length;
108 | }
109 |
110 | File[] dirs = directory.listFiles (new DirectoryFilter());
111 | if (dirs != null)
112 | {
113 | for (int i = 0; i < dirs.length; i++)
114 | total += findFiles (dirs[i], filter, collection);
115 | }
116 |
117 | return total;
118 | }
119 |
120 | /*----------------------------------------------------------------------*\
121 | Private Methods
122 | \*----------------------------------------------------------------------*/
123 | }
124 |
--------------------------------------------------------------------------------
/src/main/java/org/clapper/util/io/package.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | org.clapper.util.io - Input/Output Classes.
7 | This package contains input- and output-related Java classes; the package
8 | is a conceptual extension of the standard
9 | java.io package.
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/main/java/org/clapper/util/logging/JavaUtilLoggingTextFormatter.java:
--------------------------------------------------------------------------------
1 | package org.clapper.util.logging;
2 |
3 | import java.util.logging.Formatter;
4 | import java.util.logging.LogRecord;
5 | import java.util.Date;
6 | import java.text.SimpleDateFormat;
7 | import java.io.PrintWriter;
8 | import java.io.StringWriter;
9 |
10 | /**
11 | * Implements a log formatter for the java.util.logging
12 | * infrastructure that formats each non-exception log record as a simple,
13 | * single-line text string. (Exception stack traces will cause a message to
14 | * consume multiple lines.) This formatter currently cannot be configured
15 | * in any way, though that may change in subsequent releases. The output
16 | * format is similar to
17 | * Log4J's
18 | * PatternLayout class, with a format of "%d %-5p (%c{1}): %m%n".
19 | */
20 | public class JavaUtilLoggingTextFormatter extends Formatter
21 | {
22 | /*----------------------------------------------------------------------*\
23 | Private Constants
24 | \*----------------------------------------------------------------------*/
25 |
26 | private static final SimpleDateFormat DATE_FORMAT =
27 | new SimpleDateFormat ("yyyy/MM/dd HH:mm:ss.SSS");
28 |
29 | /*----------------------------------------------------------------------*\
30 | Private Instance Data
31 | \*----------------------------------------------------------------------*/
32 |
33 | /*----------------------------------------------------------------------*\
34 | Constructor
35 | \*----------------------------------------------------------------------*/
36 |
37 | /**
38 | * Create a new JavaUtilLoggingTextLogFormatter.
39 | */
40 | public JavaUtilLoggingTextFormatter()
41 | {
42 | super();
43 | }
44 |
45 | /*----------------------------------------------------------------------*\
46 | Public Methods
47 | \*----------------------------------------------------------------------*/
48 |
49 | /**
50 | * Format the given log record and return the formatted string.
51 | *
52 | * The resulting formatted String will normally include a localized
53 | * and formated version of the LogRecord's message field.
54 | *
55 | * @param record the log record to be formatted
56 | *
57 | * @return the formatted log record
58 | */
59 | public String format (LogRecord record)
60 | {
61 | StringWriter sw = new StringWriter();
62 | PrintWriter pw = new PrintWriter (sw);
63 | String loggerName = record.getLoggerName();
64 | Throwable ex = record.getThrown();
65 | String message = super.formatMessage (record);
66 |
67 | pw.print (DATE_FORMAT.format (new Date (record.getMillis())));
68 | pw.print (' ');
69 | pw.print (record.getLevel().toString());
70 | pw.print (" (");
71 |
72 | // Logger name is a class name. Strip all but the last part of it.
73 |
74 | int i = loggerName.lastIndexOf (".");
75 | if ((i != -1) && (i < (loggerName.length() - 1)))
76 | loggerName = loggerName.substring (i + 1);
77 |
78 | pw.print (loggerName);
79 | pw.print (") ");
80 |
81 | if ((message != null) && (message.trim().length() > 0))
82 | pw.println (message);
83 |
84 | if (ex != null)
85 | ex.printStackTrace (pw);
86 |
87 | return sw.toString();
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/src/main/java/org/clapper/util/logging/LogLevel.java:
--------------------------------------------------------------------------------
1 | package org.clapper.util.logging;
2 |
3 | /**
4 | * Encodes legal logging level constants for the {@link Logger} class.
5 | */
6 | public enum LogLevel
7 | {
8 | /*----------------------------------------------------------------------*\
9 | Enumeration Constant Values
10 | \*----------------------------------------------------------------------*/
11 |
12 | /**
13 | * Log message at "debug" level
14 | */
15 | DEBUG,
16 |
17 | /**
18 | * Log message at "error" level
19 | */
20 | ERROR,
21 |
22 | /**
23 | * Log message at "fatal error" level
24 | */
25 | FATAL,
26 |
27 | /**
28 | * Log message at "informational message" level
29 | */
30 | INFO,
31 |
32 | /**
33 | * Log message at "trace" level
34 | */
35 | TRACE,
36 |
37 | /**
38 | * Log message at "warning" level
39 | */
40 | WARNING;
41 | }
42 |
--------------------------------------------------------------------------------
/src/main/java/org/clapper/util/logging/package.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | org.clapper.util.logging - Logging Classes.
7 | This package contains logging-related classes. The
8 | Logger class provides a front-end to
9 | the
10 | Jakarta Commons Logging
11 | API,
12 | insulating calling applications from the specifics of the underlying
13 | logging layer, while providing a simpler, thinner interface than
14 | Commons Logging. Other classes, such as
15 | JavaUtilLoggingTextFormatter ,
16 | provide useful formatters for the java.util.logging framework.
17 |
18 | WARNING: If your application installs its own class loader (e.g.,
19 | as the thread context class loader), you may have problems with
20 | both Jakarta Commons Logging and java.util.logging (which
21 | Jakarta Commons Logging uses as its default logging implementation).
22 |
23 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/src/main/java/org/clapper/util/misc/EnumerationIterator.java:
--------------------------------------------------------------------------------
1 | package org.clapper.util.misc;
2 |
3 | import java.util.Iterator;
4 | import java.util.Enumeration;
5 | import java.util.NoSuchElementException;
6 |
7 | /**
8 | * The EnumerationIterator class is an adapter that makes a
9 | * java.util.Enumeration object look and behave like a
10 | * java.util.Iterator objects. The EnumerationIterator
11 | * class implements the Iterator interface and wraps an existing
12 | * Enumeration object. This class is the conceptual opposite of
13 | * the Collections.enumeration() method in the java.util
14 | * package.
15 | *
16 | * You can also use an instance of this class to wrap an
17 | * Enumeration for use in a JDK 1.5-style for each loop.
18 | * For instance:
19 | *
20 | *
21 | * {@code
22 | * Vector v = ...
23 | * for (String s : new EnumerationIterator (v.elements()))
24 | * ...
25 | * }
26 | *
27 | *
28 | * @see java.util.Iterator
29 | * @see java.util.Enumeration
30 | */
31 | public class EnumerationIterator implements Iterator, Iterable
32 | {
33 | /*----------------------------------------------------------------------*\
34 | Private Data Elements
35 | \*----------------------------------------------------------------------*/
36 |
37 | /**
38 | * The underlying Enumeration.
39 | */
40 | private Enumeration enumeration = null;
41 |
42 | /*----------------------------------------------------------------------*\
43 | Constructor
44 | \*----------------------------------------------------------------------*/
45 |
46 | /**
47 | * Allocate a new EnumerationIterator object that will
48 | * forward its calls to the specified Enumeration .
49 | *
50 | * @param enumeration The Enumeration to which to forward calls
51 | */
52 | public EnumerationIterator (Enumeration enumeration)
53 | {
54 | this.enumeration = enumeration;
55 | }
56 |
57 | /*----------------------------------------------------------------------*\
58 | Public Methods
59 | \*----------------------------------------------------------------------*/
60 |
61 | /**
62 | * Determine whether the underlying Enumeration has more
63 | * elements.
64 | *
65 | * @return true if and only if a call to
66 | * next() will return an element,
67 | * false otherwise.
68 | *
69 | * @see #next()
70 | * @see Enumeration#hasMoreElements
71 | */
72 | public boolean hasNext()
73 | {
74 | return enumeration.hasMoreElements();
75 | }
76 |
77 | /**
78 | * Returns this iterator. Necessary for the Iterable interface.
79 | *
80 | * @return this object
81 | */
82 | public Iterator iterator()
83 | {
84 | return this;
85 | }
86 |
87 | /**
88 | * Get the next element from the underlying Enumeration .
89 | *
90 | * @return the next element from the underlying Enumeration
91 | *
92 | * @exception NoSuchElementException No more elements exist
93 | *
94 | * @see Iterator#next
95 | */
96 | public T next() throws NoSuchElementException
97 | {
98 | return enumeration.nextElement();
99 | }
100 |
101 | /**
102 | * Removes from the underlying collection the last element returned by
103 | * the iterator. Not supported by this class.
104 | *
105 | * @throws IllegalStateException doesn't
106 | * @throws UnsupportedOperationException unconditionally
107 | */
108 | public void remove()
109 | throws IllegalStateException,
110 | UnsupportedOperationException
111 | {
112 | throw new UnsupportedOperationException();
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/src/main/java/org/clapper/util/misc/ObjectRemovalEvent.java:
--------------------------------------------------------------------------------
1 | package org.clapper.util.misc;
2 |
3 | import java.util.EventObject;
4 |
5 | /**
6 | * An ObjectRemovalEvent is an event that is propagated to
7 | * certain event listeners when an object is removed from some kind of
8 | * a store or data structure. For instance, the {@link LRUMap} class supports
9 | * this event through its {@link LRUMap#addRemovalListener} method.
10 | *
11 | * @see ObjectRemovalListener
12 | * @see LRUMap#addRemovalListener
13 | */
14 | public class ObjectRemovalEvent extends EventObject
15 | {
16 | /*----------------------------------------------------------------------*\
17 | Private Static Variables
18 | \*----------------------------------------------------------------------*/
19 |
20 | /**
21 | * See JDK 1.5 version of java.io.Serializable
22 | */
23 | private static final long serialVersionUID = 1L;
24 |
25 | /*----------------------------------------------------------------------*\
26 | Constructors
27 | \*----------------------------------------------------------------------*/
28 |
29 | /**
30 | * Construct a ObjectRemovalEvent event to announce the removal
31 | * of an object from a data store.
32 | *
33 | * @param source the object being removed
34 | */
35 | public ObjectRemovalEvent (Object source)
36 | {
37 | super (source);
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/main/java/org/clapper/util/misc/ObjectRemovalListener.java:
--------------------------------------------------------------------------------
1 | package org.clapper.util.misc;
2 |
3 | import java.util.EventListener;
4 |
5 | /**
6 | * An ObjectRemovalListener is an EventListener that
7 | * can be registered with certain data store objects to receive an
8 | * {@link ObjectRemovalEvent} whenever an object is removed from the data
9 | * store.
10 | *
11 | * @see ObjectRemovalEvent
12 | * @see LRUMap#addRemovalListener
13 | */
14 | public interface ObjectRemovalListener extends EventListener
15 | {
16 | /*----------------------------------------------------------------------*\
17 | Public Methods
18 | \*----------------------------------------------------------------------*/
19 |
20 | /**
21 | * This method gets called when an object is removed from a store.
22 | *
23 | * @param event the ObjectRemovalEvent event
24 | */
25 | public void objectRemoved (ObjectRemovalEvent event);
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/org/clapper/util/misc/Package.java:
--------------------------------------------------------------------------------
1 | package org.clapper.util.misc;
2 |
3 | /**
4 | * Package-specific shared utility methods. Used only within this package.
5 | */
6 | final class Package
7 | {
8 | /*----------------------------------------------------------------------*\
9 | Constants
10 | \*----------------------------------------------------------------------*/
11 |
12 | /**
13 | * The name of the resource bundle for this package.
14 | */
15 | static final String BUNDLE_NAME = "org.clapper.util.misc.Bundle";
16 |
17 | /*----------------------------------------------------------------------*\
18 | Constructor
19 | \*----------------------------------------------------------------------*/
20 |
21 | private Package()
22 | {
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/main/java/org/clapper/util/misc/Version.java:
--------------------------------------------------------------------------------
1 | package org.clapper.util.misc;
2 |
3 | /**
4 | * Contains the software version for the org.clapper.util
5 | * library. Also contains a main program which, invoked, displays the name
6 | * of the API, the version, and detailed build information on standard
7 | * output.
8 | */
9 | public final class Version extends VersionBase
10 | {
11 | /*----------------------------------------------------------------------*\
12 | Public Constants
13 | \*----------------------------------------------------------------------*/
14 |
15 | /**
16 | * The name of the resource bundle containing the build info.
17 | */
18 | public static final String BUILD_INFO_BUNDLE_NAME
19 | = "org.clapper.util.misc.BuildInfoBundle";
20 |
21 |
22 | /*----------------------------------------------------------------------*\
23 | Private Data Items
24 | \*----------------------------------------------------------------------*/
25 |
26 | private static Version instance = new Version();
27 |
28 | /*----------------------------------------------------------------------*\
29 | Constructor
30 | \*----------------------------------------------------------------------*/
31 |
32 | private Version()
33 | {
34 | }
35 |
36 | /*----------------------------------------------------------------------*\
37 | Public Methods
38 | \*----------------------------------------------------------------------*/
39 |
40 | /**
41 | * Get an instance of this class.
42 | *
43 | * @return a singleton instance of this class.
44 | */
45 | public static Version getInstance()
46 | {
47 | return new Version();
48 | }
49 |
50 | /*----------------------------------------------------------------------*\
51 | Protected Methods
52 | \*----------------------------------------------------------------------*/
53 |
54 |
55 | /**
56 | * Get the class name of the version resource bundle, which contains
57 | * values for the product version, copyright, etc.
58 | *
59 | * @return the name of the version resource bundle
60 | */
61 | protected String getVersionBundleName()
62 | {
63 | return "org.clapper.util.misc.Bundle";
64 | }
65 |
66 | /**
67 | * Get the class name of the build info resource bundle, which contains
68 | * data about when the product was built, generated (presumably)
69 | * during the build by
70 | * {@link BuildInfo#makeBuildInfoBundle BuildInfo.makeBuildInfoBundle()}.
71 | *
72 | * @return the name of the build info resource bundle
73 | */
74 | protected String getBuildInfoBundleName()
75 | {
76 | return BUILD_INFO_BUNDLE_NAME;
77 | }
78 |
79 | /**
80 | * Get the key for the version string. This key is presumed to be
81 | * in the version resource bundle.
82 | *
83 | * @return the version string key
84 | */
85 | protected String getVersionKey()
86 | {
87 | return "api.version";
88 | }
89 |
90 | /**
91 | * Get the key for the copyright string. This key is presumed to be
92 | * in the version resource bundle.
93 | *
94 | * @return the copyright string key
95 | */
96 | protected String getCopyrightKey()
97 | {
98 | return "api.copyright";
99 | }
100 |
101 | /**
102 | * Get the key for the name of the utility or application.
103 | *
104 | * @return the key
105 | */
106 | protected String getApplicationNameKey()
107 | {
108 | return "api.name";
109 | }
110 |
111 | /*----------------------------------------------------------------------*\
112 | Main Program
113 | \*----------------------------------------------------------------------*/
114 |
115 | /**
116 | * Display the build information
117 | *
118 | * @param args command-line parameters (ignored)
119 | */
120 | public static void main (String[] args)
121 | {
122 | System.out.println(getInstance().getVersionDisplay());
123 | System.exit (0);
124 | }
125 | }
126 |
--------------------------------------------------------------------------------
/src/main/java/org/clapper/util/misc/XDate.java:
--------------------------------------------------------------------------------
1 | package org.clapper.util.misc;
2 |
3 | import java.text.DateFormat;
4 | import java.text.SimpleDateFormat;
5 |
6 | import java.util.Calendar;
7 | import java.util.GregorianCalendar;
8 | import java.util.Date;
9 | import java.util.TimeZone;
10 |
11 | /**
12 | * Version of java.util.Date that provides some extra utility
13 | * functions.
14 | */
15 | public class XDate extends Date
16 | {
17 | /*----------------------------------------------------------------------*\
18 | Constructor
19 | \*----------------------------------------------------------------------*/
20 |
21 | /**
22 | * Create a new XDate so that it represents the time the
23 | * object was constructed, measure to the nearest millisecond.
24 | */
25 | public XDate()
26 | {
27 | super();
28 | }
29 |
30 | /**
31 | * Create a new XDate object and initialize it to represent the
32 | * specified number of milliseconds since the epoch.
33 | *
34 | * @param millis the milliseconds
35 | */
36 | public XDate (long millis)
37 | {
38 | super (millis);
39 | }
40 |
41 | /**
42 | * Create a new XDate object and initialize it to represent the
43 | * time contained in the specified, existing Date object (which
44 | * may, itself, be an XDate ).
45 | *
46 | * @param date the date
47 | */
48 | public XDate (Date date)
49 | {
50 | super (date.getTime());
51 | }
52 |
53 | /*----------------------------------------------------------------------*\
54 | Public Methods
55 | \*----------------------------------------------------------------------*/
56 |
57 | /**
58 | * Convert this date from its time zone to another. Sample use:
59 | *
60 | *
61 | * // Convert time in default time zone to UTC
62 | *
63 | * XDate now = new XDate();
64 | * TimeZone tzUTC = TimeZone.getTimeZone ("UTC");
65 | * Date utc = now.convertToTimeZone (tzUTC);
66 | * DateFormat fmt = new SimpleDateFormat ("yyyy/MM/dd HH:mm:ss z");
67 | * fmt.setTimeZone (tzUTC);
68 | * System.out.println (fmt.format (utc));
69 | *
70 | *
71 | * Or, more simply:
72 | *
73 | *
74 | * XDate now = new XDate();
75 | * System.out.println (now.formatInTimeZone ("yyyy/MM/dd HH:mm:ss z",
76 | * TimeZone.getTimeZone ("UTC")));
77 | *
78 | *
79 | * @param tz the time zone to convert the date to
80 | *
81 | * @return a new XDate object, appropriately converted. This
82 | * result can safely be stored in a java.util.Date
83 | * reference.
84 | *
85 | * @see #formatInTimeZone
86 | */
87 | public XDate convertToTimeZone (TimeZone tz)
88 | {
89 | Calendar calFrom = new GregorianCalendar (TimeZone.getDefault());
90 | Calendar calTo = new GregorianCalendar (tz);
91 | calFrom.setTimeInMillis (getTime());
92 | calTo.setTimeInMillis (calFrom.getTimeInMillis());
93 | return new XDate (calTo.getTime());
94 | }
95 |
96 | /**
97 | * Convenience method to produce a printable date in a specified time
98 | * zone, using a SimpleDateFormat . Calling this method is
99 | * roughly equivalent to:
100 | *
101 | *
102 | * XDate now = new XDate();
103 | * TimeZone tzUTC = TimeZone.getTimeZone ("UTC");
104 | * Date utc = now.convertToTimeZone (tzUTC);
105 | * DateFormat fmt = new SimpleDateFormat ("yyyy/MM/dd HH:mm:ss z");
106 | * fmt.setTimeZone (tzUTC);
107 | * String formattedDate = fmt.format (utc);
108 | *
109 | *
110 | * @param dateFormat the date format string to use, in a form that's
111 | * compatible with java.text.SimpleDateFormat
112 | * @param tz the desired time zone
113 | *
114 | * @return the formatted date string
115 | */
116 | public String formatInTimeZone (String dateFormat, TimeZone tz)
117 | {
118 | Date tzDate = convertToTimeZone (tz);
119 | DateFormat fmt = new SimpleDateFormat (dateFormat);
120 | fmt.setTimeZone (tz);
121 | return fmt.format (tzDate);
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/src/main/java/org/clapper/util/misc/package.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | org.clapper.util.misc - Miscellaneous Utility Classes.
7 | This package contains miscellaneous utility class.
8 | It is a conceptual extension of the standard java.util package.
9 |
10 |
11 |
--------------------------------------------------------------------------------
/src/main/java/org/clapper/util/regex/Package.java:
--------------------------------------------------------------------------------
1 | package org.clapper.util.regex;
2 |
3 | /**
4 | * Package-specific shared utility methods. Used only within this package.
5 | */
6 | final class Package
7 | {
8 | /*----------------------------------------------------------------------*\
9 | Constants
10 | \*----------------------------------------------------------------------*/
11 |
12 | /**
13 | * The name of the resource bundle for this package.
14 | */
15 | static final String BUNDLE_NAME = "org.clapper.util.regex.Bundle";
16 |
17 | /*----------------------------------------------------------------------*\
18 | Constructor
19 | \*----------------------------------------------------------------------*/
20 |
21 | private Package()
22 | {
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/main/java/org/clapper/util/text/MultipleMapVariableDereferencer.java:
--------------------------------------------------------------------------------
1 | package org.clapper.util.text;
2 |
3 | import java.util.ArrayList;
4 | import java.util.Collection;
5 | import java.util.Map;
6 |
7 | /**
8 |
9 | /**
10 | * The MultipleMapVariableDereferencer class implements the
11 | * VariableDereferencer interface and resolves variable
12 | * references by looking them up in one or more supplied Map
13 | * objects.
14 | *
15 | * The keys and values in the supplied Map objects
16 | * must be String objects.
17 | *
18 | * If more than one contained map has a value for a specific key, the
19 | * first map's value will be used. ("First", in this case, means the first
20 | * map added to the MultipleMapVariableDereference object.)
21 | *
22 | * @see VariableDereferencer
23 | * @see VariableSubstituter
24 | * @see MapVariableDereferencer
25 | */
26 | public class MultipleMapVariableDereferencer implements VariableDereferencer
27 | {
28 | /*----------------------------------------------------------------------*\
29 | Private Constants
30 | \*----------------------------------------------------------------------*/
31 |
32 | /*----------------------------------------------------------------------*\
33 | Private Instance Data
34 | \*----------------------------------------------------------------------*/
35 |
36 | private Collection> mapList =
37 | new ArrayList>();
38 |
39 | /*----------------------------------------------------------------------*\
40 | Constructor
41 | \*----------------------------------------------------------------------*/
42 |
43 | /**
44 | * Creates a new instance of MultipleMapVariableDereferencer
45 | * that has no associated Map objects. Map objects
46 | * can be added with the {@link #addMap} method.
47 | */
48 | public MultipleMapVariableDereferencer()
49 | {
50 | }
51 |
52 | /**
53 | * Creates a new instance of MultipleMapVariableDereferencer
54 | * that is initialized with a specified set of maps.
55 | *
56 | * @param maps the maps to use
57 | */
58 | public MultipleMapVariableDereferencer(Map... maps)
59 | {
60 | for (Map map : maps)
61 | addMap(map);
62 | }
63 |
64 | /*----------------------------------------------------------------------*\
65 | Public Methods
66 | \*----------------------------------------------------------------------*/
67 |
68 | /**
69 | * Add a map to the list of maps to use when dereferencing variable values.
70 | * The map is added to the end of the list.
71 | *
72 | * @param map The map to add
73 | */
74 | public final void addMap(Map map)
75 | {
76 | mapList.add(map);
77 | }
78 |
79 | /**
80 | * Get the value associated with a given variable.
81 | *
82 | * @param varName The name of the variable for which the value is
83 | * desired.
84 | * @param context a context object, passed through from the caller
85 | * to the dereferencer, or null if there isn't one.
86 | * Ignored here.
87 | *
88 | * @return The variable's value. If the variable has no value, this
89 | * method must return null.
90 | */
91 | public String getVariableValue (String varName, Object context)
92 | {
93 | String result = null;
94 |
95 | for (Map map : mapList)
96 | {
97 | result = map.get(varName);
98 | if (result != null)
99 | break;
100 | }
101 |
102 | return result;
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/src/main/java/org/clapper/util/text/Package.java:
--------------------------------------------------------------------------------
1 | package org.clapper.util.text;
2 |
3 | /**
4 | * Package-specific shared utility methods. Used only within this package.
5 | */
6 | final class Package
7 | {
8 | /*----------------------------------------------------------------------*\
9 | Constants
10 | \*----------------------------------------------------------------------*/
11 |
12 | /**
13 | * The name of the resource bundle for this package.
14 | */
15 | static final String BUNDLE_NAME = "org.clapper.util.text.Bundle";
16 |
17 | /*----------------------------------------------------------------------*\
18 | Constructor
19 | \*----------------------------------------------------------------------*/
20 |
21 | private Package()
22 | {
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/main/java/org/clapper/util/text/VariableDereferencer.java:
--------------------------------------------------------------------------------
1 | package org.clapper.util.text;
2 |
3 | /**
4 | * The VariableDereferencer
interface defines the behavior
5 | * of classes that can look up variables by name, returning their values.
6 | * It is used primarily to mark classes that can work hand-in-hand with
7 | * VariableSubstituter
objects to resolve variable references
8 | * in strings.
9 | *
10 | * The values for referenced variables can come from anywhere (in a
11 | * Properties
object, via direct method calls, from a symbol
12 | * table, etc.), provided the values can be located using only the
13 | * variable's name.
14 | *
15 | * @see MapVariableDereferencer
16 | * @see VariableSubstituter
17 | */
18 | public interface VariableDereferencer
19 | {
20 | /*----------------------------------------------------------------------*\
21 | Public Methods
22 | \*----------------------------------------------------------------------*/
23 |
24 | /**
25 | * Get the value associated with a given variable.
26 | *
27 | * @param varName The name of the variable for which the value is
28 | * desired.
29 | * @param context a context object, passed through from the caller
30 | * to the dereferencer, or null if there isn't one.
31 | *
32 | * @return The variable's value. If the variable has no value, this
33 | * method must return null.
34 | *
35 | * @throws VariableSubstitutionException substitution error
36 | */
37 | public String getVariableValue (String varName, Object context)
38 | throws VariableSubstitutionException;
39 | }
40 |
--------------------------------------------------------------------------------
/src/main/java/org/clapper/util/text/VariableNameChecker.java:
--------------------------------------------------------------------------------
1 | package org.clapper.util.text;
2 |
3 | /**
4 | * This interface defines the methods for a class that checks characters
5 | * to determine whether they're legal for a variable name that's to be
6 | * substituted by a {@link VariableSubstituter} object. It has a single
7 | * method, {@link #legalVariableCharacter}, which determines whether a
8 | * specified character is a legal part of a variable name or not. This
9 | * capability provides additional flexibility by allowing callers to define
10 | * precisely what characters constitute legal variable names.
11 | *
12 | * @see VariableSubstituter
13 | * @see VariableDereferencer
14 | */
15 | public interface VariableNameChecker
16 | {
17 | /*----------------------------------------------------------------------*\
18 | Required Methods
19 | \*----------------------------------------------------------------------*/
20 |
21 | /**
22 | * Determine whether a character may legally be used in a variable
23 | * name or not.
24 | *
25 | * @param c The character to test
26 | *
27 | * @return true if the character may be part of a variable name,
28 | * false otherwise
29 | *
30 | * @see VariableSubstituter#substitute
31 | */
32 | public boolean legalVariableCharacter (char c);
33 | }
34 |
--------------------------------------------------------------------------------
/src/main/java/org/clapper/util/text/package.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | org.clapper.util.text - Text and String Classes.
7 | This package contains text and string-related Java classes; the package
8 | is a conceptual extension of the standard
9 | java.text package.
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/main/resources/META-INF/version.prop:
--------------------------------------------------------------------------------
1 | version=${project.version}
2 |
--------------------------------------------------------------------------------
/src/main/resources/ModTheSpire.json:
--------------------------------------------------------------------------------
1 | {
2 | "modid": "modthespire",
3 | "name": "ModTheSpire",
4 | "author_list": [],
5 | "description": "YOU SHOULD NOT BE SEEING MODTHESPIRE HERE. If you see this, remove ModTheSpire from your 'mods/' directory.",
6 | "mts_version": "999.999.999"
7 | }
8 |
--------------------------------------------------------------------------------
/src/main/resources/assets/ajax-loader.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kiooeht/ModTheSpire/c6201995b8f5d97620cdb00f28a724a95b7742a6/src/main/resources/assets/ajax-loader.gif
--------------------------------------------------------------------------------
/src/main/resources/assets/download.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kiooeht/ModTheSpire/c6201995b8f5d97620cdb00f28a724a95b7742a6/src/main/resources/assets/download.gif
--------------------------------------------------------------------------------
/src/main/resources/assets/error.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kiooeht/ModTheSpire/c6201995b8f5d97620cdb00f28a724a95b7742a6/src/main/resources/assets/error.gif
--------------------------------------------------------------------------------
/src/main/resources/assets/good.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kiooeht/ModTheSpire/c6201995b8f5d97620cdb00f28a724a95b7742a6/src/main/resources/assets/good.gif
--------------------------------------------------------------------------------
/src/main/resources/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kiooeht/ModTheSpire/c6201995b8f5d97620cdb00f28a724a95b7742a6/src/main/resources/assets/icon.png
--------------------------------------------------------------------------------
/src/main/resources/assets/update.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kiooeht/ModTheSpire/c6201995b8f5d97620cdb00f28a724a95b7742a6/src/main/resources/assets/update.gif
--------------------------------------------------------------------------------
/src/main/resources/assets/warning.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kiooeht/ModTheSpire/c6201995b8f5d97620cdb00f28a724a95b7742a6/src/main/resources/assets/warning.gif
--------------------------------------------------------------------------------
/src/main/resources/assets/workshop.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kiooeht/ModTheSpire/c6201995b8f5d97620cdb00f28a724a95b7742a6/src/main/resources/assets/workshop.gif
--------------------------------------------------------------------------------
/src/main/resources/steam_appid.txt:
--------------------------------------------------------------------------------
1 | 646570
--------------------------------------------------------------------------------