this repo has no description
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

migrate database prototype inside jar

clover a42bdabe 1dd33b0c

+133 -68
+5
CHANGELOG.md
··· 4 4 5 5 ## Next Release 6 6 7 + ### Changed 8 + - Main config and logs moved to system user directory (%AppData% on Windows, etc) 9 + - Added nix compatibility 10 + 7 11 ### Fixed 8 12 - (Map Editor) Fixed crash when fusing vertices 13 + - (Map Editor) (MacOS) Fixed mouse capture when accessibility permissions are not enabled 9 14 - (Sprite Editor) Copy animation works properly
+1 -5
build.gradle.kts
··· 166 166 description = "Create zip file for Star Rod release" 167 167 168 168 from(shadowJar.get().outputs.files) 169 - 170 - from(file("database")) { 171 - into("database") 172 - } 173 169 174 170 from(file(licenseBuildDir)) { 175 - into("database/licenses") 171 + into("licenses") 176 172 } 177 173 178 174 from(file("exec")) {
database/editor/EditorGuides.json src/main/resources/database/editor/EditorGuides.json
database/editor/template_map.xml src/main/resources/database/editor/template_map.xml
database/star_rod_macros.h src/main/resources/database/star_rod_macros.h
database/themes/README.txt src/main/resources/database/themes/README.txt
+2 -3
src/main/java/app/Directories.java
··· 13 13 //======================================================================================= 14 14 // Directories not related to any specific project 15 15 16 - SEED_DATABASE (Root.NONE, "/database/"), // Read-only database that comes with the jar 17 - DATABASE (Root.CONFIG, "/database/"), 16 + DATABASE (Root.CONFIG, "/database/"), 18 17 DATABASE_EDITOR (Root.CONFIG, DATABASE, "/editor/"), 19 18 DATABASE_THEMES (Root.CONFIG, DATABASE, "/themes/"), 20 19 21 - TEMP (Root.STATE, "/temp/"), 22 20 LOGS (Root.STATE, "/logs/"), 21 + TEMP (Root.STATE, "/temp/"), 23 22 24 23 //======================================================================================= 25 24 // Directories contain dumped content needed for Star Rod to function
+18 -28
src/main/java/app/Environment.java
··· 23 23 24 24 import javax.imageio.ImageIO; 25 25 import javax.swing.ImageIcon; 26 - import javax.swing.JOptionPane; 27 26 import javax.swing.SwingUtilities; 28 27 29 28 import org.apache.commons.io.FileExistsException; ··· 176 175 try { 177 176 Manifest manifest = new Manifest(cl.getResourceAsStream("META-INF/MANIFEST.MF")); 178 177 Attributes attr = manifest.getMainAttributes(); 179 - 178 + 180 179 versionString = attr.getValue("App-Version"); 181 180 gitBuildBranch = attr.getValue("Build-Branch"); 182 181 gitBuildCommit = attr.getValue("Build-Commit"); 183 182 gitBuildTag = attr.getValue("Build-Tag"); 184 183 185 184 // Git info not available when built with Nix; normalise empty strings to null 186 - if (gitBuildBranch != null && gitBuildBranch.isEmpty()) gitBuildBranch = null; 187 - if (gitBuildCommit != null && gitBuildCommit.isEmpty()) gitBuildCommit = null; 188 - if (gitBuildTag != null && gitBuildTag.isEmpty()) gitBuildTag = null; 189 - 185 + if (gitBuildBranch != null && gitBuildBranch.isEmpty()) 186 + gitBuildBranch = null; 187 + if (gitBuildCommit != null && gitBuildCommit.isEmpty()) 188 + gitBuildCommit = null; 189 + if (gitBuildTag != null && gitBuildTag.isEmpty()) 190 + gitBuildTag = null; 191 + 190 192 Logger.logf("Detected version %s (%s-%s)", versionString, gitBuildBranch, gitBuildCommit); 191 193 } 192 194 catch (IOException | IndexOutOfBoundsException e) { ··· 213 215 getUserStateDir().mkdirs(); 214 216 215 217 try { 216 - checkForDependencies(); 218 + unpackDatabase(); 217 219 File projDir = readMainConfig(); 218 220 219 221 if (projDir == null) { ··· 321 323 } 322 324 } 323 325 324 - private static final void checkForDependencies() throws IOException 326 + private static final void unpackDatabase() throws IOException 325 327 { 326 - File db = Directories.SEED_DATABASE.toFile(); 327 - 328 - if (!db.exists() || !db.isDirectory()) { 329 - SwingUtils.getErrorDialog() 330 - .setTitle("Missing Directory") 331 - .setMessage("Could not find required directory: " + db.getName(), 332 - "It should be in the same directory as the jar.") 333 - .show(); 334 - 335 - exit(); 336 - } 328 + File dbDir = Directories.DATABASE.toFile(); 329 + if (!dbDir.exists()) 330 + dbDir.mkdirs(); 337 331 338 - // Copy SEED_DATABASE to DATABASE if DATABASE does not exist 339 - // TODO: handle upgrades 340 - File writeDb = Directories.DATABASE.toFile(); 341 - if (!writeDb.exists()) { 342 - writeDb.mkdirs(); 343 - FileUtils.copyDirectory(db, writeDb); 344 - } 332 + Resource.copyMissing(ResourceType.ProtoDatabase, dbDir); 345 333 } 346 334 347 335 public static final File getUserConfigDir() 348 336 { 349 337 String userHome = System.getProperty("user.home"); 350 338 351 - if (isWindows()) return new File(userHome, "/AppData/Local/StarRod/"); 339 + if (isWindows()) 340 + return new File(userHome, "/AppData/Local/StarRod/"); 352 341 353 342 String xdgConfigHome = System.getenv("XDG_CONFIG_HOME"); 354 343 String dotConfig = (xdgConfigHome != null && !xdgConfigHome.isEmpty()) ··· 361 350 { 362 351 String userHome = System.getProperty("user.home"); 363 352 364 - if (isWindows()) return new File(userHome, "/AppData/Local/StarRod/"); 353 + if (isWindows()) 354 + return new File(userHome, "/AppData/Local/StarRod/"); 365 355 366 356 String xdgStateHome = System.getenv("XDG_STATE_HOME"); 367 357 String dotState = (xdgStateHome != null && !xdgStateHome.isEmpty())
+5 -4
src/main/java/app/LoadingBar.java
··· 5 5 import java.awt.RenderingHints; 6 6 import java.awt.image.BufferedImage; 7 7 import java.io.IOException; 8 + import java.util.List; 8 9 import java.util.concurrent.ThreadLocalRandom; 9 10 10 11 import javax.imageio.ImageIO; ··· 88 89 bimg = ImageIO.read(Resource.getStream(ResourceType.Deluxe, "splash.jpg")); 89 90 } 90 91 else { 91 - String[] splashNames = Resource.getResourceNames(ResourceType.Splash); 92 + List<String> splashNames = Resource.getResourceNames(ResourceType.Splash); 92 93 93 - if (splashNames.length > 0) { 94 - int randomNum = ThreadLocalRandom.current().nextInt(0, splashNames.length); 95 - bimg = ImageIO.read(Resource.getStream(ResourceType.Splash, splashNames[randomNum])); 94 + if (splashNames.size() > 0) { 95 + int randomNum = ThreadLocalRandom.current().nextInt(0, splashNames.size()); 96 + bimg = ImageIO.read(Resource.getStream(ResourceType.Splash, splashNames.get(randomNum))); 96 97 } 97 98 } 98 99
+55 -26
src/main/java/app/Resource.java
··· 1 1 package app; 2 2 3 3 import java.io.File; 4 + import java.io.FileOutputStream; 4 5 import java.io.IOException; 5 6 import java.io.InputStream; 7 + import java.io.OutputStream; 6 8 import java.net.URISyntaxException; 7 9 import java.net.URL; 8 10 import java.net.URLDecoder; 11 + import java.nio.file.Files; 12 + import java.nio.file.Path; 13 + import java.nio.file.Paths; 9 14 import java.util.ArrayList; 10 - import java.util.Enumeration; 11 - import java.util.HashSet; 12 - import java.util.Set; 15 + import java.util.List; 13 16 import java.util.jar.JarEntry; 14 17 import java.util.jar.JarFile; 15 18 ··· 25 28 { 26 29 // @formatter:off 27 30 EditorAsset ("editor/"), 31 + ProtoDatabase ("database/"), 28 32 Extract ("extract/"), 29 33 EntityModelRoots ("entity/"), 30 34 Font ("font/"), ··· 119 123 } 120 124 } 121 125 122 - public static String[] getResourceNames(ResourceType type) 126 + public static List<String> getResourceNames(ResourceType type) 123 127 { 124 128 try { 125 - return getResourceListing(Resource.class, type.path); 129 + return getAllResourceNames(Resource.class, type.path); 126 130 } 127 131 catch (Exception e) { 128 132 Logger.printStackTrace(e); 129 - return new String[] {}; 133 + return new ArrayList<String>(); 130 134 } 131 135 } 132 136 ··· 134 138 * List directory contents for a resource folder. Not recursive. 135 139 * This is basically a brute-force implementation. 136 140 * Works for regular files and also JARs. 141 + * Modified from a method written by Greg Briggs 137 142 * 138 - * @author Greg Briggs 139 143 * @param clazz Any java class that lives in the same place as the resources you want. 140 144 * @param path Should end with "/", but not start with one. 141 145 * @return Just the name of each member item, not the full paths. 142 146 * @throws URISyntaxException 143 147 * @throws IOException 144 148 */ 145 - private static String[] getResourceListing(Class<?> clazz, String path) throws URISyntaxException, IOException 149 + private static List<String> getAllResourceNames(Class<?> clazz, String path) throws IOException 146 150 { 147 151 URL dirURL = clazz.getClassLoader().getResource(path); 148 152 if (dirURL != null && dirURL.getProtocol().equals("file")) { 149 - // A file path: easy enough 150 - return new File(dirURL.toURI()).list(); 153 + // resource exists as a file 154 + try { 155 + Path dirPath = Paths.get(dirURL.toURI()); 156 + return Files.walk(dirPath) 157 + .filter(Files::isRegularFile) 158 + .map(filePath -> dirPath.relativize(filePath).toString().replace("\\", "/")) 159 + .toList(); 160 + } 161 + catch (URISyntaxException e) { 162 + Logger.printStackTrace(e); 163 + return new ArrayList<String>(); 164 + } 151 165 } 152 166 153 167 if (dirURL == null) { ··· 158 172 } 159 173 160 174 if (dirURL.getProtocol().equals("jar")) { 161 - // A JAR path 162 - String jarPath = dirURL.getPath().substring(5, dirURL.getPath().indexOf("!")); //strip out only the JAR file 175 + // resource exists within the jar 176 + String jarPath = dirURL.getPath().substring(5, dirURL.getPath().indexOf("!")); // strip out only the JAR file 163 177 try (JarFile jar = new JarFile(URLDecoder.decode(jarPath, "UTF-8"))) { 164 - Enumeration<JarEntry> entries = jar.entries(); //gives ALL entries in jar 165 - Set<String> result = new HashSet<String>(); //avoid duplicates in case it is a subdirectory 166 - while (entries.hasMoreElements()) { 167 - String name = entries.nextElement().getName(); 168 - if (name.startsWith(path)) { //filter according to the path 169 - String entry = name.substring(path.length()); 170 - int checkSubdir = entry.indexOf("/"); 171 - if (checkSubdir >= 0) { 172 - // if it is a subdirectory, we just return the directory name 173 - entry = entry.substring(0, checkSubdir); 178 + return jar.stream() 179 + .map(JarEntry::getName) 180 + .filter(name -> name.startsWith(path) && !name.endsWith("/")) // only files 181 + .map(name -> name.substring(path.length())) // strip out path prefix 182 + .map(name -> name.startsWith("/") ? name.substring(1) : name) // remove leading slash if present 183 + .toList(); 184 + } 185 + } 186 + 187 + throw new UnsupportedOperationException("Cannot list files for URL " + dirURL); 188 + } 189 + 190 + public static void copyMissing(ResourceType type, File dir) throws IOException 191 + { 192 + for (String filename : getAllResourceNames(Resource.class, type.path)) { 193 + // the destination file in the user's database directory 194 + File targetFile = new File(Directories.DATABASE.toFile(), filename); 195 + targetFile.getParentFile().mkdirs(); 196 + 197 + try (InputStream resourceStream = Resource.getStream(type, filename)) { 198 + if (resourceStream != null) { 199 + // copy the file if it doesn't exist 200 + if (!targetFile.exists()) { 201 + try (OutputStream outStream = new FileOutputStream(targetFile)) { 202 + byte[] buffer = new byte[4096]; 203 + int bytesRead; 204 + while ((bytesRead = resourceStream.read(buffer)) != -1) { 205 + outStream.write(buffer, 0, bytesRead); 206 + } 174 207 } 175 - result.add(entry); 176 208 } 177 209 } 178 - return result.toArray(new String[result.size()]); 179 210 } 180 211 } 181 - 182 - throw new UnsupportedOperationException("Cannot list files for URL " + dirURL); 183 212 } 184 213 }
+45 -2
src/main/java/app/StarRodMain.java
··· 187 187 188 188 JButton extractDataButton = new JButton("Extract Map Data"); 189 189 trySetIcon(extractDataButton, ExpectedAsset.ICON_EXTRACT); 190 - SwingUtils.setFontSize(themesMenuButton, 12); 190 + SwingUtils.setFontSize(extractDataButton, 12); 191 191 extractDataButton.addActionListener((e) -> { 192 192 action_extractMapData(); 193 193 }); ··· 197 197 /* 198 198 JButton captureThumbnailsButton = new JButton("Capture Thumbnails"); 199 199 trySetIcon(captureThumbnailsButton, ExpectedAsset.ICON_THEMES); 200 - SwingUtils.setFontSize(themesMenuButton, 12); 200 + SwingUtils.setFontSize(captureThumbnailsButton, 12); 201 201 captureThumbnailsButton.addActionListener((e) -> { 202 202 action_captureThumbnails(); 203 203 }); 204 204 buttons.add(captureThumbnailsButton); 205 205 */ 206 + 207 + JButton openConfigDirButton = new JButton("Open Config Dir"); 208 + trySetIcon(openConfigDirButton, ExpectedAsset.ICON_SILVER); 209 + SwingUtils.setFontSize(openConfigDirButton, 12); 210 + openConfigDirButton.addActionListener((e) -> { 211 + action_openDir(Environment.getUserConfigDir()); 212 + }); 213 + buttons.add(openConfigDirButton); 214 + 215 + JButton openProjectDirButton = new JButton("Open Project Dir"); 216 + trySetIcon(openProjectDirButton, ExpectedAsset.ICON_GOLD); 217 + SwingUtils.setFontSize(openProjectDirButton, 12); 218 + openProjectDirButton.addActionListener((e) -> { 219 + action_openDir(Environment.getProjectDirectory()); 220 + }); 221 + buttons.add(openProjectDirButton); 206 222 207 223 consoleTextArea = new JTextArea(); 208 224 consoleTextArea.setRows(8); ··· 277 293 278 294 add(themesMenuButton, "grow"); 279 295 add(extractDataButton, "grow"); 296 + 297 + add(openConfigDirButton, "grow"); 298 + add(openProjectDirButton, "grow"); 280 299 281 300 add(progressPanel, "grow, span, wrap, gap top 8"); 282 301 add(consoleScrollPane, "grow, span"); ··· 446 465 editor.shutdownThumbnail(); 447 466 } 448 467 }); 468 + } 469 + 470 + private void action_openDir(File dir) 471 + { 472 + if (dir.exists()) { 473 + try { 474 + Desktop desktop = Desktop.getDesktop(); 475 + desktop.open(dir); 476 + } 477 + catch (IOException e) { 478 + Logger.printStackTrace(e); 479 + SwingUtils.getWarningDialog() 480 + .setTitle("IOException") 481 + .setMessage(e.getMessage()) 482 + .show(); 483 + } 484 + } 485 + else { 486 + Toolkit.getDefaultToolkit().beep(); 487 + SwingUtils.getWarningDialog() 488 + .setTitle("Directory Not Found") 489 + .setMessage("Could not find:", dir.getAbsolutePath()) 490 + .show(); 491 + } 449 492 } 450 493 451 494 public static void handleEarlyCrash(Throwable e)
+2
src/main/java/assets/ExpectedAsset.java
··· 15 15 ICON_IMAGE_EDITOR (AssetSubdir.ICON, "food/TastyTonic.png", true), 16 16 ICON_THEMES (AssetSubdir.ICON, "badge/PUpDDown.png", true), 17 17 ICON_EXTRACT (AssetSubdir.ICON, "badge/Peekaboo.png", true), 18 + ICON_GOLD (AssetSubdir.ICON, "key/card_gold.png", true), 19 + ICON_SILVER (AssetSubdir.ICON, "key/card_silver.png", true), 18 20 ICON_APP (AssetSubdir.ICON, "battle/ShootingStar.png", true), 19 21 ICON_X (AssetSubdir.ICON, "battle/XBandage.png", true), 20 22 CIRCLE_SHADOW (AssetSubdir.ENTITY, "shadow/circle.png", false),