diff --git a/.classpath b/.classpath index 34382c4..5e81577 100644 --- a/.classpath +++ b/.classpath @@ -1,18 +1,19 @@ - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + diff --git a/hacked-libtiled/tiled/core/TileSet.java b/hacked-libtiled/tiled/core/TileSet.java index afd99af..fcacaf7 100644 --- a/hacked-libtiled/tiled/core/TileSet.java +++ b/hacked-libtiled/tiled/core/TileSet.java @@ -98,27 +98,42 @@ public class TileSet implements Iterable File f = new File(imgFilename); - Image image = ImageIO.read(f.getCanonicalFile()); + BufferedImage image = ImageIO.read(f.getCanonicalFile()); if (image == null) { - throw new IOException("Failed to load " + tilebmpFile); + throw new IOException("Failed to load " + imgFilename); } - Toolkit tk = Toolkit.getDefaultToolkit(); + tilebmpFile = f; + tileDimensions = new Rectangle(cutter.getTileDimensions()); + +// Toolkit tk = Toolkit.getDefaultToolkit(); +// +// if (transparentColor != null) { +// int rgb = transparentColor.getRGB(); +// image = tk.createImage( +// new FilteredImageSource(image.getSource(), +// new TransparentImageFilter(rgb))); +// } +// +// BufferedImage buffered = new BufferedImage( +// image.getWidth(null), +// image.getHeight(null), +// BufferedImage.TYPE_INT_ARGB); +// buffered.getGraphics().drawImage(image, 0, 0, null); - if (transparentColor != null) { - int rgb = transparentColor.getRGB(); - image = tk.createImage( - new FilteredImageSource(image.getSource(), - new TransparentImageFilter(rgb))); - } + importTileBitmap(image, cutter); + } + + public void weakImportTileBitmap(String imgFilename, TileCutter cutter) + throws IOException + { + setTilesetImageFilename(imgFilename); + + File f = new File(imgFilename); - BufferedImage buffered = new BufferedImage( - image.getWidth(null), - image.getHeight(null), - BufferedImage.TYPE_INT_ARGB); - buffered.getGraphics().drawImage(image, 0, 0, null); + tilebmpFile = f; + tileDimensions = new Rectangle(cutter.getTileDimensions()); - importTileBitmap(buffered, cutter); } public void loadFromProject(String name, TMXMap tmxMap, int tileWidth, int tileHeight) { diff --git a/minify/com/whoischarles/util/json/Minify.java b/minify/com/whoischarles/util/json/Minify.java new file mode 100644 index 0000000..bc7c688 --- /dev/null +++ b/minify/com/whoischarles/util/json/Minify.java @@ -0,0 +1,405 @@ +/** + * ---------------------- + * Minify.java 2015-10-04 + * ---------------------- + * + * Copyright (c) 2015 Charles Bihis (www.whoischarles.com) + * + * This work is an adaptation of JSMin.java published by John Reilly which is a translation from C to Java of jsmin.c + * published by Douglas Crockford. Permission is hereby granted to use this Java version under the same conditions as + * the original jsmin.c on which all of these derivatives are based. + * + * + * + * --------------------- + * JSMin.java 2006-02-13 + * --------------------- + * + * Copyright (c) 2006 John Reilly (www.inconspicuous.org) + * + * This work is a translation from C to Java of jsmin.c published by Douglas Crockford. Permission is hereby granted to + * use the Java version under the same conditions as the jsmin.c on which it is based. + * + * + * + * ------------------ + * jsmin.c 2003-04-21 + * ------------------ + * + * Copyright (c) 2002 Douglas Crockford (www.crockford.com) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the + * Software. + * + * The Software shall be used for Good, not Evil. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.whoischarles.util.json; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PushbackInputStream; +import java.nio.charset.StandardCharsets; + +/** + * Minify.java is written by Charles Bihis (www.whoischarles.com) and is adapted from JSMin.java written by John Reilly + * (www.inconspicuous.org) which is itself a translation of jsmin.c written by Douglas Crockford (www.crockford.com). + * + * @see http://www.unl.edu/ucomm/templatedependents/JSMin.java + * @see http://www.crockford.com/javascript/jsmin.c + */ +public class Minify { + + private static final int EOF = -1; + + private PushbackInputStream in; + private OutputStream out; + private int currChar; + private int nextChar; + private int line; + private int column; + + public static enum Action { + OUTPUT_CURR, DELETE_CURR, DELETE_NEXT + } + + public Minify() { + this.in = null; + this.out = null; + } + + /** + * Minifies the input JSON string. + * + * Takes the input JSON string and deletes the characters which are insignificant to JavaScipt. Comments will be + * removed, tabs will be replaced with spaces, carriage returns will be replaced with line feeds, and most spaces + * and line feeds will be removed. The result will be returned. + * + * @param json The JSON string for which to minify + * @return A minified, yet functionally identical, version of the input JSON string + */ + public String minify(String json) { + InputStream in = new ByteArrayInputStream(json.getBytes(StandardCharsets.UTF_8)); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + try { + minify(in, out); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + + return out.toString().trim(); + } + + /** + * Takes an input stream to a JSON string and outputs minified JSON to the output stream. + * + * Takes the input JSON via the input stream and deletes the characters which are insignificant to JavaScript. + * Comments will be removed, tabs will be replaced with spaaces, carriage returns will be replaced with line feeds, + * and most spaces and line feeds will be removed. The result is streamed to the output stream. + * + * @param in The InputStream from which to get the un-minified JSON + * @param out The OutputStream where the resulting minified JSON will be streamed to + * @throws IOException + * @throws UnterminatedRegExpLiteralException + * @throws UnterminatedCommentException + * @throws UnterminatedStringLiteralException + */ + public void minify(InputStream in, OutputStream out) throws IOException, UnterminatedRegExpLiteralException, + UnterminatedCommentException, + UnterminatedStringLiteralException { + + // Initialize + this.in = new PushbackInputStream(in); + this.out = out; + this.line = 0; + this.column = 0; +// currChar = '\n'; +// action(Action.DELETE_NEXT); + currChar = get(); + nextChar = peek(); + + // Process input + while (currChar != EOF) { + switch (currChar) { + + case ' ': + if (isAlphanum(nextChar)) { + action(Action.OUTPUT_CURR); + } else { + action(Action.DELETE_CURR); + } + break; + + case '\n': + switch (nextChar) { + case '{': + case '[': + case '(': + case '+': + case '-': + action(Action.OUTPUT_CURR); + break; + case ' ': + action(Action.DELETE_NEXT); + break; + default: + if (isAlphanum(nextChar)) { + action(Action.OUTPUT_CURR); + } else { + action(Action.DELETE_CURR); + } + } + break; + + default: + switch (nextChar) { + case ' ': + if (isAlphanum(currChar)) { + action(Action.OUTPUT_CURR); + break; + } + action(Action.DELETE_NEXT); + break; + case '\n': + switch (currChar) { + case '}': + case ']': + case ')': + case '+': + case '-': + case '"': + case '\'': + action(Action.OUTPUT_CURR); + break; + default: + if (isAlphanum(currChar)) { + action(Action.OUTPUT_CURR); + } else { + action(Action.DELETE_NEXT); + } + } + break; + default: + action(Action.OUTPUT_CURR); + break; + } + } + } + out.flush(); + } + + /** + * Process the current character with an appropriate action. + * + * The action that occurs is determined by the current character. The options are: + * + * 1. Output currChar: output currChar, copy nextChar to currChar, get the next character and save it to nextChar + * 2. Delete currChar: copy nextChar to currChar, get the next character and save it to nextChar + * 3. Delete nextChar: get the next character and save it to nextChar + * + * This method essentially treats a string as a single character. Also recognizes regular expressions if they are + * preceded by '(', ',', or '='. + * + * @param action The action to perform + * @throws IOException + * @throws UnterminatedRegExpLiteralException + * @throws UnterminatedCommentException + * @throws UnterminatedStringLiteralException + */ + private void action(Action action) throws IOException, UnterminatedRegExpLiteralException, UnterminatedCommentException, + UnterminatedStringLiteralException { + + // Process action + switch (action) { + + case OUTPUT_CURR: + out.write(currChar); + + case DELETE_CURR: + currChar = nextChar; + + if (currChar == '\'' || currChar == '"') { + for ( ; ; ) { + out.write(currChar); + currChar = get(); + if (currChar == nextChar) { + break; + } + if (currChar <= '\n') { + throw new UnterminatedStringLiteralException(line, + column); + } + if (currChar == '\\') { + out.write(currChar); + currChar = get(); + } + } + } + + case DELETE_NEXT: + nextChar = next(); + if (nextChar == '/' + && (currChar == '(' || currChar == ',' || currChar == '=' || currChar == ':')) { + out.write(currChar); + out.write(nextChar); + for ( ; ; ) { + currChar = get(); + if (currChar == '/') { + break; + } else if (currChar == '\\') { + out.write(currChar); + currChar = get(); + } else if (currChar <= '\n') { + throw new UnterminatedRegExpLiteralException(line, + column); + } + out.write(currChar); + } + nextChar = next(); + } + } + } + + /** + * Determines whether a given character is a letter, digit, underscore, dollar sign, or non-ASCII character. + * + * @param c The character to compare + * @return True if the character is a letter, digit, underscore, dollar sign, or non-ASCII character. False otherwise. + */ + private boolean isAlphanum(int c) { + return ((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') + || c == '_' || c == '$' || c == '\\' || c > 126); + } + + /** + * Returns the next character from the input stream. + * + * Will pop the next character from the input stack. If the character is a control character, translate it to a space + * or line feed. + * + * @return The next character from the input stream + * @throws IOException + */ + private int get() throws IOException { + int c = in.read(); + + if (c == '\n') { + line++; + column = 0; + } else { + column++; + } + + if (c >= ' ' || c == '\n' || c == EOF) { + return c; + } + + if (c == '\r') { + column = 0; + return '\n'; + } + + return ' '; + } + + /** + * Returns the next character from the input stream without popping it from the stack. + * + * @return The next character from the input stream + * @throws IOException + */ + private int peek() throws IOException { + int lookaheadChar = in.read(); + in.unread(lookaheadChar); + return lookaheadChar; + } + + /** + * Get the next character from the input stream, excluding comments. + * + * Will read from the input stream via the get() method. Will exclude characters that are part of + * comments. peek() is used to se if a '/' is followed by a '/' or a '*' for the purpose of identifying + * comments. + * + * @return The next character from the input stream, excluding characters from comments + * @throws IOException + * @throws UnterminatedCommentException + */ + private int next() throws IOException, UnterminatedCommentException { + int c = get(); + + if (c == '/') { + switch (peek()) { + + case '/': + for ( ; ; ) { + c = get(); + if (c <= '\n') { + return c; + } + } + + case '*': + get(); + for ( ; ; ) { + switch (get()) { + case '*': + if (peek() == '/') { + get(); + return ' '; + } + break; + case EOF: + throw new UnterminatedCommentException(line, column); + } + } + + default: + return c; + } + + } + return c; + } + + /** + * Exception to be thrown when an unterminated comment appears in the input. + */ + public static class UnterminatedCommentException extends Exception { + public UnterminatedCommentException(int line, int column) { + super("Unterminated comment at line " + line + " and column " + column); + } + } + + /** + * Exception to be thrown when an unterminated string literal appears in the input. + */ + public static class UnterminatedStringLiteralException extends Exception { + public UnterminatedStringLiteralException(int line, int column) { + super("Unterminated string literal at line " + line + " and column " + column); + } + } + + /** + * Exception to be thrown when an unterminated regular expression literal appears in the input. + */ + public static class UnterminatedRegExpLiteralException extends Exception { + public UnterminatedRegExpLiteralException(int line, int column) { + super("Unterminated regular expression at line " + line + " and column " + column); + } + } +} \ No newline at end of file diff --git a/src/com/gpl/rpg/atcontentstudio/ui/tools/i18n/PoPotWriter.java b/src/com/gpl/rpg/atcontentstudio/model/tools/i18n/PoPotWriter.java similarity index 94% rename from src/com/gpl/rpg/atcontentstudio/ui/tools/i18n/PoPotWriter.java rename to src/com/gpl/rpg/atcontentstudio/model/tools/i18n/PoPotWriter.java index 507f0a4..9a836e8 100644 --- a/src/com/gpl/rpg/atcontentstudio/ui/tools/i18n/PoPotWriter.java +++ b/src/com/gpl/rpg/atcontentstudio/model/tools/i18n/PoPotWriter.java @@ -1,4 +1,4 @@ -package com.gpl.rpg.atcontentstudio.ui.tools.i18n; +package com.gpl.rpg.atcontentstudio.model.tools.i18n; import java.io.File; import java.io.FileWriter; diff --git a/src/com/gpl/rpg/atcontentstudio/ui/tools/i18n/PotComparator.java b/src/com/gpl/rpg/atcontentstudio/model/tools/i18n/PotComparator.java similarity index 93% rename from src/com/gpl/rpg/atcontentstudio/ui/tools/i18n/PotComparator.java rename to src/com/gpl/rpg/atcontentstudio/model/tools/i18n/PotComparator.java index 3702545..916b3aa 100644 --- a/src/com/gpl/rpg/atcontentstudio/ui/tools/i18n/PotComparator.java +++ b/src/com/gpl/rpg/atcontentstudio/model/tools/i18n/PotComparator.java @@ -1,4 +1,4 @@ -package com.gpl.rpg.atcontentstudio.ui.tools.i18n; +package com.gpl.rpg.atcontentstudio.model.tools.i18n; import java.io.File; import java.io.FileFilter; @@ -24,15 +24,15 @@ import net.launchpad.tobal.poparser.POParser; * To use this, paste the following script in the beanshell console of ATCS. * Don't forget to change the project number to suit your needs. * - * import com.gpl.rpg.atcontentstudio.model.Workspace; - * import com.gpl.rpg.atcontentstudio.ui.tools.i18n.PotGenerator; - * import com.gpl.rpg.atcontentstudio.ui.tools.i18n.PotComparator; - * - * proj = Workspace.activeWorkspace.projects.get(7); - * PotGenerator.generatePotFileForProject(proj); - * comp = new PotComparator(proj); - * comp.compare(); - * comp.updatePoFiles(proj); +import com.gpl.rpg.atcontentstudio.model.Workspace; +import com.gpl.rpg.atcontentstudio.model.tools.i18n.PotGenerator; +import com.gpl.rpg.atcontentstudio.model.tools.i18n.PotComparator; + +proj = Workspace.activeWorkspace.projects.get(7); +PotGenerator.generatePotFileForProject(proj); +comp = new PotComparator(proj); +comp.compare(); +comp.updatePoFiles(proj); * * * diff --git a/src/com/gpl/rpg/atcontentstudio/ui/tools/i18n/PotGenerator.java b/src/com/gpl/rpg/atcontentstudio/model/tools/i18n/PotGenerator.java similarity index 96% rename from src/com/gpl/rpg/atcontentstudio/ui/tools/i18n/PotGenerator.java rename to src/com/gpl/rpg/atcontentstudio/model/tools/i18n/PotGenerator.java index 00cf3fe..df42692 100644 --- a/src/com/gpl/rpg/atcontentstudio/ui/tools/i18n/PotGenerator.java +++ b/src/com/gpl/rpg/atcontentstudio/model/tools/i18n/PotGenerator.java @@ -1,4 +1,4 @@ -package com.gpl.rpg.atcontentstudio.ui.tools.i18n; +package com.gpl.rpg.atcontentstudio.model.tools.i18n; import java.io.File; import java.util.LinkedHashMap; diff --git a/src/com/gpl/rpg/atcontentstudio/model/tools/resoptimizer/ResourcesCompactor.java b/src/com/gpl/rpg/atcontentstudio/model/tools/resoptimizer/ResourcesCompactor.java new file mode 100644 index 0000000..b294fdd --- /dev/null +++ b/src/com/gpl/rpg/atcontentstudio/model/tools/resoptimizer/ResourcesCompactor.java @@ -0,0 +1,381 @@ +package com.gpl.rpg.atcontentstudio.model.tools.resoptimizer; + +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.image.BufferedImage; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileFilter; +import java.io.FileInputStream; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.StringWriter; +import java.nio.CharBuffer; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import javax.imageio.ImageIO; + +import org.json.simple.JSONArray; + +import com.gpl.rpg.atcontentstudio.io.JsonPrettyWriter; +import com.gpl.rpg.atcontentstudio.model.GameDataElement; +import com.gpl.rpg.atcontentstudio.model.GameSource; +import com.gpl.rpg.atcontentstudio.model.Project; +import com.gpl.rpg.atcontentstudio.model.gamedata.ActorCondition; +import com.gpl.rpg.atcontentstudio.model.gamedata.GameDataSet; +import com.gpl.rpg.atcontentstudio.model.gamedata.Item; +import com.gpl.rpg.atcontentstudio.model.gamedata.NPC; +import com.gpl.rpg.atcontentstudio.model.maps.TMXMap; +import com.gpl.rpg.atcontentstudio.model.maps.TMXMapSet; +import com.gpl.rpg.atcontentstudio.model.sprites.SpriteSheetSet; +import com.gpl.rpg.atcontentstudio.utils.FileUtils; +import com.whoischarles.util.json.Minify; +import com.whoischarles.util.json.Minify.UnterminatedCommentException; +import com.whoischarles.util.json.Minify.UnterminatedRegExpLiteralException; +import com.whoischarles.util.json.Minify.UnterminatedStringLiteralException; + +import tiled.core.TileSet; +import tiled.io.TMXMapWriter; + +/** + * + * @author Kevin + * + * To use this, paste the following script in the beanshell console of ATCS. + * Don't forget to change the project number to suit your needs. + * +import com.gpl.rpg.atcontentstudio.model.tools.resoptimizer.ResourcesCompactor; +import com.gpl.rpg.atcontentstudio.model.Workspace; + +proj = Workspace.activeWorkspace.projects.get(0); +new ResourcesCompactor(proj).compactData(); + * + */ +public class ResourcesCompactor { + + public static String DEFAULT_REL_PATH_IN_PROJECT = "compressed"+File.separator; + + private Project proj; + private File baseFolder; + private List compressedSpritesheets = new LinkedList(); + private List preservedSpritesheets = new LinkedList(); + + private Map spritesRelocationForObjects = new LinkedHashMap(); + private Integer currentSpritesheetIndexForObjects = 0; + private CompressedSpritesheet currentSpritesheetForObjects = null; + + private Map spritesRelocationForMaps = new LinkedHashMap(); + private Map spritesheetsBySidForMaps = new LinkedHashMap(); + private Integer currentSpritesheetIndexForMaps = 0; + private CompressedSpritesheet currentSpritesheetForMaps = null; + + public ResourcesCompactor(Project proj) { + this.proj = proj; + this.baseFolder = new File(proj.baseFolder, DEFAULT_REL_PATH_IN_PROJECT); + if (!baseFolder.exists()) baseFolder.mkdirs(); + } + + public void compactData() { + compactJsonData(); + for(CompressedSpritesheet cs : compressedSpritesheets) { + cs.drawFile(); + } + for (File preserved : preservedSpritesheets) { + FileUtils.copyFile(preserved, new File(baseFolder.getAbsolutePath()+File.separator+DEFAULT_DRAWABLE_REL_PATH+File.separator+preserved.getName())); + } + compactMaps(); + } + + public void compactJsonData() { + final List filesCovered = new LinkedList(); + + File folder = new File(baseFolder.getAbsolutePath()+File.separator+GameDataSet.DEFAULT_REL_PATH_IN_SOURCE); + if (!folder.exists()) folder.mkdirs(); + + for (ActorCondition ac : proj.baseContent.gameData.actorConditions) { + if (filesCovered.contains(ac.jsonFile)) continue; + File currentFile = ac.jsonFile; + filesCovered.add(currentFile); + List dataToSave = new ArrayList(); + for (ActorCondition acond : proj.baseContent.gameData.actorConditions) { + if (!acond.jsonFile.equals(currentFile)) continue; + Map json = acond.toJson(); + json.put("iconID", convertObjectSprite(acond.icon_id).toStringID()); + dataToSave.add(json); + } + File target = new File(folder, ac.jsonFile.getName()); + writeJson(dataToSave, target); + } + + for (Item it : proj.baseContent.gameData.items) { + if (filesCovered.contains(it.jsonFile)) continue; + File currentFile = it.jsonFile; + filesCovered.add(currentFile); + List dataToSave = new ArrayList(); + for (Item item : proj.baseContent.gameData.items) { + if (!item.jsonFile.equals(currentFile)) continue; + Map json = item.toJson(); + json.put("iconID", convertObjectSprite(item.icon_id).toStringID()); + dataToSave.add(json); + } + File target = new File(folder, it.jsonFile.getName()); + writeJson(dataToSave, target); + } + + + for (NPC np : proj.baseContent.gameData.npcs) { + if (filesCovered.contains(np.jsonFile)) continue; + File currentFile = np.jsonFile; + filesCovered.add(currentFile); + List dataToSave = new ArrayList(); + for (NPC npc : proj.baseContent.gameData.npcs) { + if (!npc.jsonFile.equals(currentFile)) continue; + Map json = npc.toJson(); + if (proj.getImage(npc.icon_id).getWidth(null) == TILE_WIDTH_IN_PIXELS || proj.getImage(npc.icon_id).getHeight(null) == TILE_HEIGHT_IN_PIXELS) { + json.put("iconID", convertObjectSprite(npc.icon_id).toStringID()); + } + dataToSave.add(json); + } + File target = new File(folder, np.jsonFile.getName()); + writeJson(dataToSave, target); + } + + File[] remainingFiles = proj.baseContent.gameData.baseFolder.listFiles(new FileFilter() { + @Override + public boolean accept(File arg0) { + return arg0.getName().endsWith(".json") && !filesCovered.contains(arg0); + } + }); + + for (File source : remainingFiles) { + File target = new File(folder, source.getName()); + minifyJson(source, target); + } + } + + private Minify jsonMinifier = new Minify(); + + private void writeJson(List dataToSave, File target) { + StringWriter writer = new JsonPrettyWriter(); + try { + JSONArray.writeJSONString(dataToSave, writer); + } catch (IOException e) { + //Impossible with a StringWriter + } + String toWrite = writer.toString(); + try { + FileWriter w = new FileWriter(target); + w.write(jsonMinifier.minify(toWrite)); + w.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + private void minifyJson(File source, File target) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try { + FileInputStream fis = new FileInputStream(source); + jsonMinifier.minify(fis, baos); + FileWriter w = new FileWriter(target); + w.write(baos.toString()); + w.close(); + } catch (IOException e) { + e.printStackTrace(); + } catch (UnterminatedRegExpLiteralException e) { + e.printStackTrace(); + } catch (UnterminatedCommentException e) { + e.printStackTrace(); + } catch (UnterminatedStringLiteralException e) { + e.printStackTrace(); + } + } + private void compactMaps() { + for (TMXMap map : proj.baseContent.gameMaps.tmxMaps) { + TMXMap clone = map.clone(); + for (GameDataElement gde : clone.getBacklinks()) { + gde.removeBacklink(clone); + } + clone.getBacklinks().clear(); + tiled.core.Map tmx = clone.tmxMap; + compactMap(tmx, map.id); + clone.tmxMap = null; + clone.groups.clear(); + clone = null; + } + } + + private void compactMap(tiled.core.Map tmx, String name) { + File target = new File(baseFolder.getAbsolutePath()+File.separator+TMXMapSet.DEFAULT_REL_PATH_IN_SOURCE+File.separator+name+".tmx"); + if (!target.getParentFile().exists()) target.getParentFile().mkdirs(); + + Map localConvertions = new LinkedHashMap(); + List usedSpritesheets = new LinkedList(); + + List toRemove = new LinkedList(); + + for (tiled.core.TileSet ts : tmx.getTileSets()) { + if (!ts.getName().equalsIgnoreCase("map_dynamic_placeholders")) { + toRemove.add(ts); + } + } + + for (tiled.core.TileLayer layer : tmx.getTileLayers()) { + for (int x = 0; x < layer.getWidth(); x++) { + for (int y = 0; y < layer.getHeight(); y++) { + tiled.core.Tile tile = layer.getTileAt(x, y); + if (tile != null && !tile.getTileSet().getName().equalsIgnoreCase("map_dynamic_placeholders")) { + SpritesheetId sid = convertMapSprite(SpritesheetId.toStringID(tile.getTileSet().getName(), tile.getId())); + localConvertions.put(tile, sid); + if (!usedSpritesheets.contains(spritesheetsBySidForMaps.get(sid))) { + usedSpritesheets.add(spritesheetsBySidForMaps.get(sid)); + } + } + } + } + } + + Map csToTs = new LinkedHashMap(); + for (CompressedSpritesheet cs : usedSpritesheets) { + cs.drawFile(); + tiled.core.TileSet ts = new tiled.core.TileSet(); + csToTs.put(cs, ts); + tiled.util.BasicTileCutter cutter = new tiled.util.BasicTileCutter(TILE_WIDTH_IN_PIXELS, TILE_HEIGHT_IN_PIXELS, 0, 0); + try { + ts.importTileBitmap(cs.f.getAbsolutePath(), cutter); + } catch (IOException e) { + e.printStackTrace(); + } + ts.setName(cs.prefix+Integer.toString(cs.index)); + //ts.setSource("../drawable/"+ts.getName()+TILESHEET_SUFFIX); + tmx.addTileset(ts); + } + + for (tiled.core.TileLayer layer : tmx.getTileLayers()) { + for (tiled.core.Tile tile : localConvertions.keySet()) { + SpritesheetId sid = localConvertions.get(tile); + layer.replaceTile(tile, csToTs.get(spritesheetsBySidForMaps.get(sid)).getTile(sid.offset)); + } + } + + for (tiled.core.TileSet ts : toRemove) { + tmx.removeTileset(ts); + } + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + TMXMapWriter writer = new TMXMapWriter(); + writer.settings.layerCompressionMethod = TMXMapWriter.Settings.LAYER_COMPRESSION_METHOD_ZLIB; + try { + writer.writeMap(tmx, baos, target.getAbsolutePath()); + String xml = baos.toString(); + FileWriter w = new FileWriter(target); + w.write(xml); + w.close(); + } catch (Exception e) { + e.printStackTrace(); + } + + } + + private SpritesheetId convertObjectSprite(String originalSpriteId) { + if (spritesRelocationForObjects.containsKey(SpritesheetId.getInstance(originalSpriteId))) { + return spritesRelocationForObjects.get(SpritesheetId.getInstance(originalSpriteId)); + } else if (currentSpritesheetForObjects == null || !currentSpritesheetForObjects.hasFreeSlot()) { + currentSpritesheetForObjects = new CompressedSpritesheet(TILESHEET_PREFIX_FOR_OBJECTS, currentSpritesheetIndexForObjects); + compressedSpritesheets.add(currentSpritesheetForObjects); + currentSpritesheetIndexForObjects++; + } + SpritesheetId sid = currentSpritesheetForObjects.addSprite(originalSpriteId); + spritesRelocationForObjects.put(SpritesheetId.getInstance(originalSpriteId), sid); + return sid; + } + + private SpritesheetId convertMapSprite(String originalSpriteId) { + if (spritesRelocationForMaps.containsKey(SpritesheetId.getInstance(originalSpriteId))) { + return spritesRelocationForMaps.get(SpritesheetId.getInstance(originalSpriteId)); + } else if (currentSpritesheetForMaps == null || !currentSpritesheetForMaps.hasFreeSlot()) { + currentSpritesheetForMaps = new CompressedSpritesheet(TILESHEET_PREFIX_FOR_MAPS, currentSpritesheetIndexForMaps); + compressedSpritesheets.add(currentSpritesheetForMaps); + currentSpritesheetIndexForMaps++; + } + SpritesheetId sid = currentSpritesheetForMaps.addSprite(originalSpriteId); + spritesRelocationForMaps.put(SpritesheetId.getInstance(originalSpriteId), sid); + spritesheetsBySidForMaps.put(sid, currentSpritesheetForMaps); + return sid; + } + + + private static final int TILESHEET_WIDTH_IN_SPRITES = 8; + private static final int TILESHEET_HEIGHT_IN_SPRITES = 8; + private static final int TILE_WIDTH_IN_PIXELS = 32; + private static final int TILE_HEIGHT_IN_PIXELS = 32; + + private static final String TILESHEET_PREFIX_FOR_OBJECTS = "obj_"; + private static final String TILESHEET_PREFIX_FOR_MAPS = "map_"; + private static final String TILESHEET_SUFFIX = ".png"; + + private static final String DEFAULT_DRAWABLE_REL_PATH = SpriteSheetSet.DEFAULT_REL_PATH_IN_SOURCE; + + private class CompressedSpritesheet { + String prefix; + int index; + File f; + + + boolean mustDraw = true; + int nextFreeSlot = 0; + String[] originalSpritesId = new String[TILESHEET_WIDTH_IN_SPRITES * TILESHEET_HEIGHT_IN_SPRITES]; + + public CompressedSpritesheet(String prefix, int index) { + this.prefix = prefix; + this.index = index; + + File folder = new File(ResourcesCompactor.this.baseFolder.getAbsolutePath()+File.separator+DEFAULT_DRAWABLE_REL_PATH); + if (!folder.exists()) folder.mkdirs(); + this.f = new File(folder, prefix+Integer.toString(index)+TILESHEET_SUFFIX); + } + + public boolean hasFreeSlot() { + return nextFreeSlot < TILESHEET_WIDTH_IN_SPRITES * TILESHEET_HEIGHT_IN_SPRITES; + } + + public SpritesheetId addSprite(String spriteId) { + mustDraw = true; + originalSpritesId[nextFreeSlot] = spriteId; + nextFreeSlot++; + return SpritesheetId.getInstance(prefix+Integer.toString(index), nextFreeSlot - 1); + } + + + public void drawFile() { + if (!mustDraw) return; + BufferedImage img = new BufferedImage(TILESHEET_WIDTH_IN_SPRITES * TILE_WIDTH_IN_PIXELS, TILESHEET_HEIGHT_IN_SPRITES * TILE_HEIGHT_IN_PIXELS, BufferedImage.TYPE_INT_ARGB); + Graphics2D g = (Graphics2D)img.getGraphics(); + Color transparent = new Color(0, 0, 0, 0); + g.setColor(transparent); + g.fillRect(0, 0, img.getWidth(), img.getHeight()); + for (int i = 0; i < nextFreeSlot; i++) { + g.drawImage( + proj.getImage(originalSpritesId[i]), + (i % TILESHEET_WIDTH_IN_SPRITES) * TILE_WIDTH_IN_PIXELS, + (i / TILESHEET_WIDTH_IN_SPRITES) * TILE_HEIGHT_IN_PIXELS, + TILE_WIDTH_IN_PIXELS, + TILE_HEIGHT_IN_PIXELS, + null); + } + try { + ImageIO.write(img, "png", f); + mustDraw = false; + } catch (IOException e) { + e.printStackTrace(); + } + } + } + +} + + \ No newline at end of file diff --git a/src/com/gpl/rpg/atcontentstudio/model/tools/resoptimizer/SpritesheetId.java b/src/com/gpl/rpg/atcontentstudio/model/tools/resoptimizer/SpritesheetId.java new file mode 100644 index 0000000..1200da7 --- /dev/null +++ b/src/com/gpl/rpg/atcontentstudio/model/tools/resoptimizer/SpritesheetId.java @@ -0,0 +1,38 @@ +package com.gpl.rpg.atcontentstudio.model.tools.resoptimizer; + +import java.util.LinkedHashMap; +import java.util.Map; + +public class SpritesheetId { + static Map instancesCache = new LinkedHashMap(); + + String tileset; + int offset; + + static SpritesheetId getInstance(String id) { + String[] values = id.split(":"); + return getInstance(values[0], Integer.parseInt(values[1])); + } + + static SpritesheetId getInstance(String tilesetId, int offset) { + if (!instancesCache.containsKey(toStringID(tilesetId, offset))) { + SpritesheetId instance = new SpritesheetId(tilesetId, offset); + instancesCache.put(instance.toStringID(), instance); + } + return instancesCache.get(toStringID(tilesetId, offset)); + } + + private SpritesheetId(String tileset, int offset) { + this.tileset = tileset; + this.offset = offset; + } + + public String toStringID() { + return toStringID(tileset, offset); + } + + static String toStringID(String tileset, int offset) { + return tileset+":"+Integer.toString(offset); + } + +} diff --git a/src/com/gpl/rpg/atcontentstudio/ui/tools/BeanShellView.java b/src/com/gpl/rpg/atcontentstudio/ui/tools/BeanShellView.java index 4984898..fbca8b7 100644 --- a/src/com/gpl/rpg/atcontentstudio/ui/tools/BeanShellView.java +++ b/src/com/gpl/rpg/atcontentstudio/ui/tools/BeanShellView.java @@ -22,6 +22,7 @@ import bsh.EvalError; import bsh.Interpreter; import com.gpl.rpg.atcontentstudio.ui.DefaultIcons; +import com.gpl.rpg.atcontentstudio.ui.WorkerDialog; import com.jidesoft.swing.JideBoxLayout; public class BeanShellView extends JFrame { @@ -85,17 +86,29 @@ public class BeanShellView extends JFrame { run.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - Interpreter shInt = new Interpreter(); - PrintStream printOut = new PrintStream(new AreaOutputStream(outArea)); + final Interpreter shInt = new Interpreter(); + final PrintStream printOut = new PrintStream(new AreaOutputStream(outArea)); shInt.setOut(printOut); - PrintStream printErr = new PrintStream(new AreaOutputStream(errArea)); + final PrintStream printErr = new PrintStream(new AreaOutputStream(errArea)); shInt.setErr(printErr); - try { - shInt.eval(shArea.getText()); - } catch (EvalError e1) { - e1.printStackTrace(printErr); - } + WorkerDialog.showTaskMessage("Running your script...", null, new Runnable() { + @Override + public void run() { + + try { + shInt.eval(shArea.getText()); + } catch (EvalError e1) { + e1.printStackTrace(printErr); + } + printOut.flush(); + printErr.flush(); + printOut.close(); + printErr.close(); + + } + }); + } });