From a3ffecfd23aeff4f79c8447b8a7fbd7dbfbcebc6 Mon Sep 17 00:00:00 2001 From: OMGeeky Date: Tue, 24 Jun 2025 21:50:52 +0200 Subject: [PATCH 01/15] improve json saving to file --- .../rpg/atcontentstudio/model/Project.java | 2733 ++++++++--------- .../model/WorkspaceSettings.java | 21 +- .../model/gamedata/GameDataCategory.java | 20 +- .../model/gamedata/JSONElement.java | 11 +- .../resoptimizer/ResourcesCompactor.java | 12 +- .../tools/writermode/WriterModeDataSet.java | 19 +- .../rpg/atcontentstudio/utils/FileUtils.java | 485 +-- 7 files changed, 1644 insertions(+), 1657 deletions(-) diff --git a/src/com/gpl/rpg/atcontentstudio/model/Project.java b/src/com/gpl/rpg/atcontentstudio/model/Project.java index 8ae3fe0..57c24a9 100644 --- a/src/com/gpl/rpg/atcontentstudio/model/Project.java +++ b/src/com/gpl/rpg/atcontentstudio/model/Project.java @@ -1,1373 +1,1360 @@ -package com.gpl.rpg.atcontentstudio.model; - -import com.gpl.rpg.atcontentstudio.ATContentStudio; -import com.gpl.rpg.atcontentstudio.Notification; -import com.gpl.rpg.atcontentstudio.io.JsonPrettyWriter; -import com.gpl.rpg.atcontentstudio.io.SettingsSave; -import com.gpl.rpg.atcontentstudio.model.GameSource.Type; -import com.gpl.rpg.atcontentstudio.model.bookmarks.BookmarksRoot; -import com.gpl.rpg.atcontentstudio.model.gamedata.*; -import com.gpl.rpg.atcontentstudio.model.maps.TMXMap; -import com.gpl.rpg.atcontentstudio.model.maps.TMXMapSet; -import com.gpl.rpg.atcontentstudio.model.maps.Worldmap; -import com.gpl.rpg.atcontentstudio.model.maps.WorldmapSegment; -import com.gpl.rpg.atcontentstudio.model.saves.SavedGamesSet; -import com.gpl.rpg.atcontentstudio.model.sprites.Spritesheet; -import com.gpl.rpg.atcontentstudio.model.tools.writermode.WriterModeData; -import com.gpl.rpg.atcontentstudio.ui.DefaultIcons; -import com.gpl.rpg.atcontentstudio.ui.WorkerDialog; -import com.gpl.rpg.atcontentstudio.utils.FileUtils; -import org.json.simple.JSONArray; -import org.w3c.dom.Comment; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.NodeList; -import org.xml.sax.InputSource; -import org.xml.sax.SAXException; - -import javax.swing.tree.TreeNode; -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.transform.*; -import javax.xml.transform.dom.DOMSource; -import javax.xml.transform.stream.StreamResult; -import javax.xml.transform.stream.StreamSource; -import java.awt.*; -import java.io.*; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.List; -import java.util.*; - -public class Project implements ProjectTreeNode, Serializable { - - private static final long serialVersionUID = 4807454973303366758L; - private static final String drawablePath = TMXMapSet.DEFAULT_REL_PATH_TO_DRAWABLE.replace("\\", "/"); - - - //Every instance field that is not transient will be saved in this file. - public static final String SETTINGS_FILE = ".project"; - - public String name; - - public File baseFolder; - public boolean open; - - public GameSource baseContent; //A.k.a library - - public GameSource referencedContent; //Pointers to base content - public transient GameSource alteredContent; //Copied from base content (does not overwrite yet) - public transient GameSource createdContent; //Stand-alone. - public transient BookmarksRoot bookmarks; - - - public SavedGamesSet saves; //For simulations. - - public transient SavedSlotCollection v; - - public transient Workspace parent; - - public Properties knownSpritesheetsProperties; - - public static enum ResourceSet { - gameData, - debugData, - allFiles - } - - public ResourceSet sourceSetToUse; - - public Project(Workspace w, String name, File source, ResourceSet sourceSet) { - this.parent = w; - this.name = name; - this.sourceSetToUse = sourceSet; - - //CREATE PROJECT - baseFolder = new File(w.baseFolder, name + File.separator); - try { - baseFolder.mkdir(); - } catch (SecurityException e) { - Notification.addError("Eror creating project root folder: " + e.getMessage()); - e.printStackTrace(); - } - open = true; - v = new SavedSlotCollection(); - - knownSpritesheetsProperties = new Properties(); - try { - knownSpritesheetsProperties.load(Project.class.getResourceAsStream("/spritesheets.properties")); - } catch (IOException e) { - Notification.addWarn("Unable to load default spritesheets properties."); - e.printStackTrace(); - } - - - baseContent = new GameSource(source, this); - -// referencedContent = new GameSource(this, GameSource.Type.referenced); - - alteredContent = new GameSource(this, GameSource.Type.altered); - createdContent = new GameSource(this, GameSource.Type.created); - bookmarks = new BookmarksRoot(this); - - saves = new SavedGamesSet(this); - - v.add(createdContent); - v.add(alteredContent); -// v.add(referencedContent); - v.add(baseContent); - v.add(saves); - v.add(bookmarks); - - linkAll(); - - save(); - } - - - @Override - public TreeNode getChildAt(int childIndex) { - return v.getNonEmptyElementAt(childIndex); - } - - @Override - public int getChildCount() { - return v.getNonEmptySize(); - } - - @Override - public TreeNode getParent() { - return parent; - } - - @Override - public int getIndex(TreeNode node) { - return v.getNonEmptyIndexOf((ProjectTreeNode) node); - } - - @Override - public boolean getAllowsChildren() { - return open; - } - - @Override - public boolean isLeaf() { - return !open; - } - - @Override - public Enumeration children() { - return v.getNonEmptyElements(); - } - - @Override - public void childrenAdded(List path) { - path.add(0, this); - parent.childrenAdded(path); - } - - @Override - public void childrenChanged(List path) { - path.add(0, this); - parent.childrenChanged(path); - } - - @Override - public void childrenRemoved(List path) { - path.add(0, this); - parent.childrenRemoved(path); - } - - @Override - public void notifyCreated() { - childrenAdded(new ArrayList()); - for (ProjectTreeNode node : v.getNonEmptyIterable()) { - node.notifyCreated(); - } - } - - @Override - public String getDesc() { - return (needsSaving() ? "*" : "") + name; - } - - - public void close() { - this.open = false; - childrenRemoved(new ArrayList()); - } - - public void open() { - open = true; - } - - - public static Project fromFolder(Workspace w, File projRoot) { - Project p; - File f = new File(projRoot, Project.SETTINGS_FILE); - if (!f.exists()) { - Notification.addError("Unable to find " + SETTINGS_FILE + " for project " + projRoot.getName()); - return null; - } else { - p = (Project) SettingsSave.loadInstance(f, "Project"); - } - p.refreshTransients(w); - return p; - } - - public void refreshTransients(Workspace w) { - this.parent = w; - - projectElementListeners = new HashMap, List>(); - - try { - knownSpritesheetsProperties = new Properties(); - knownSpritesheetsProperties.load(Project.class.getResourceAsStream("/spritesheets.properties")); - } catch (IOException e) { - Notification.addWarn("Unable to load default spritesheets properties."); - e.printStackTrace(); - } - - if (sourceSetToUse == null) { - sourceSetToUse = ResourceSet.allFiles; - } - -// long l = new Date().getTime(); - baseContent.refreshTransients(this); -// l = new Date().getTime() - l; -// System.out.println("All initialized in "+l+"ms."); -// referencedContent.refreshTransients(this); - alteredContent = new GameSource(this, GameSource.Type.altered); - createdContent = new GameSource(this, GameSource.Type.created); - bookmarks = new BookmarksRoot(this); - - saves.refreshTransients(); - - v = new SavedSlotCollection(); - v.add(createdContent); - v.add(alteredContent); -// v.add(referencedContent); - v.add(baseContent); - v.add(saves); - v.add(bookmarks); - - - linkAll(); - - } - - public void linkAll() { - for (ProjectTreeNode node : baseContent.gameData.v.getNonEmptyIterable()) { - if (node instanceof GameDataCategory) { - for (GameDataElement e : ((GameDataCategory) node)) { - e.link(); - } - } - } - for (ProjectTreeNode node : baseContent.gameMaps.tmxMaps) { - ((TMXMap) node).link(); - } - for (ProjectTreeNode node : alteredContent.gameData.v.getNonEmptyIterable()) { - if (node instanceof GameDataCategory) { - for (GameDataElement e : ((GameDataCategory) node)) { - e.link(); - } - } - } - for (ProjectTreeNode node : alteredContent.gameMaps.tmxMaps) { - ((TMXMap) node).link(); - } - for (ProjectTreeNode node : createdContent.gameData.v.getNonEmptyIterable()) { - if (node instanceof GameDataCategory) { - for (GameDataElement e : ((GameDataCategory) node)) { - e.link(); - } - } - } - for (ProjectTreeNode node : createdContent.gameMaps.tmxMaps) { - ((TMXMap) node).link(); - } - - for (WorldmapSegment node : createdContent.worldmap) { - node.link(); - } - for (WorldmapSegment node : alteredContent.worldmap) { - node.link(); - } - for (WorldmapSegment node : baseContent.worldmap) { - node.link(); - } - } - - public void save() { - SettingsSave.saveInstance(this, new File(baseFolder, Project.SETTINGS_FILE), "Project " + this.name); - } - - - public JSONElement getGameDataElement(Class gdeClass, String id) { - if (gdeClass == ActorCondition.class) { - return getActorCondition(id); - } - if (gdeClass == Dialogue.class) { - return getDialogue(id); - } - if (gdeClass == Droplist.class) { - return getDroplist(id); - } - if (gdeClass == ItemCategory.class) { - return getItemCategory(id); - } - if (gdeClass == Item.class) { - return getItem(id); - } - if (gdeClass == NPC.class) { - return getNPC(id); - } - if (gdeClass == Quest.class) { - return getQuest(id); - } - return null; - } - - public int getNodeCount(Class gdeClass) { - if (gdeClass == ActorCondition.class) { - return getActorConditionCount(); - } - if (gdeClass == Dialogue.class) { - return getDialogueCount(); - } - if (gdeClass == Droplist.class) { - return getDroplistCount(); - } - if (gdeClass == ItemCategory.class) { - return getItemCategoryCount(); - } - if (gdeClass == Item.class) { - return getItemCount(); - } - if (gdeClass == NPC.class) { - return getNPCCount(); - } - if (gdeClass == Quest.class) { - return getQuestCount(); - } - return 0; - } - - public int getNodeIndex(GameDataElement node) { - Class gdeClass = node.getClass(); - if (gdeClass == ActorCondition.class) { - return getActorConditionIndex((ActorCondition) node); - } - if (gdeClass == Dialogue.class) { - return getDialogueIndex((Dialogue) node); - } - if (gdeClass == Droplist.class) { - return getDroplistIndex((Droplist) node); - } - if (gdeClass == ItemCategory.class) { - return getItemCategoryIndex((ItemCategory) node); - } - if (gdeClass == Item.class) { - return getItemIndex((Item) node); - } - if (gdeClass == NPC.class) { - return getNPCIndex((NPC) node); - } - if (gdeClass == Quest.class) { - return getQuestIndex((Quest) node); - } - return 0; - } - - public ActorCondition getActorCondition(String id) { - ActorCondition gde = createdContent.gameData.getActorCondition(id); - if (gde == null) gde = alteredContent.gameData.getActorCondition(id); - if (gde == null) gde = baseContent.gameData.getActorCondition(id); - return gde; - } - - public int getActorConditionCount() { - return createdContent.gameData.actorConditions.size() + baseContent.gameData.actorConditions.size(); - } - - public ActorCondition getActorCondition(int index) { - if (index < createdContent.gameData.actorConditions.size()) { - return createdContent.gameData.actorConditions.get(index); - } else if (index < getActorConditionCount()) { - return getActorCondition(baseContent.gameData.actorConditions.get(index - createdContent.gameData.actorConditions.size()).id); - } - return null; - } - - public int getActorConditionIndex(ActorCondition ac) { - if (ac.getDataType() == GameSource.Type.created) { - return createdContent.gameData.actorConditions.getIndex(ac); - } else { - return createdContent.gameData.actorConditions.size() + baseContent.gameData.actorConditions.indexOf(baseContent.gameData.getActorCondition(ac.id)); - } - } - - - public Dialogue getDialogue(String id) { - Dialogue gde = createdContent.gameData.getDialogue(id); - if (gde == null) gde = alteredContent.gameData.getDialogue(id); - if (gde == null) gde = baseContent.gameData.getDialogue(id); - return gde; - } - - public int getDialogueCount() { - return createdContent.gameData.dialogues.size() + baseContent.gameData.dialogues.size(); - } - - public Dialogue getDialogue(int index) { - if (index < createdContent.gameData.dialogues.size()) { - return createdContent.gameData.dialogues.get(index); - } else if (index < getDialogueCount()) { - return getDialogue(baseContent.gameData.dialogues.get(index - createdContent.gameData.dialogues.size()).id); - } - return null; - } - - public int getDialogueIndex(Dialogue dialogue) { - if (dialogue.getDataType() == GameSource.Type.created) { - return createdContent.gameData.dialogues.getIndex(dialogue); - } else { - return createdContent.gameData.dialogues.size() + baseContent.gameData.dialogues.indexOf(baseContent.gameData.getDialogue(dialogue.id)); - } - } - - - public Droplist getDroplist(String id) { - Droplist gde = createdContent.gameData.getDroplist(id); - if (gde == null) gde = alteredContent.gameData.getDroplist(id); - if (gde == null) gde = baseContent.gameData.getDroplist(id); - return gde; - } - - public int getDroplistCount() { - return createdContent.gameData.droplists.size() + baseContent.gameData.droplists.size(); - } - - public Droplist getDroplist(int index) { - if (index < createdContent.gameData.droplists.size()) { - return createdContent.gameData.droplists.get(index); - } else if (index < getDroplistCount()) { - return getDroplist(baseContent.gameData.droplists.get(index - createdContent.gameData.droplists.size()).id); - } - return null; - } - - public int getDroplistIndex(Droplist droplist) { - if (droplist.getDataType() == GameSource.Type.created) { - return createdContent.gameData.droplists.getIndex(droplist); - } else { - return createdContent.gameData.droplists.size() + baseContent.gameData.droplists.indexOf(baseContent.gameData.getDroplist(droplist.id)); - } - } - - - public Item getItem(String id) { - Item gde = createdContent.gameData.getItem(id); - if (gde == null) gde = alteredContent.gameData.getItem(id); - if (gde == null) gde = baseContent.gameData.getItem(id); - return gde; - } - - public int getItemCount() { - return createdContent.gameData.items.size() + baseContent.gameData.items.size(); - } - - public Item getItem(int index) { - if (index < createdContent.gameData.items.size()) { - return createdContent.gameData.items.get(index); - } else if (index < getItemCount()) { - return getItem(baseContent.gameData.items.get(index - createdContent.gameData.items.size()).id); - } - return null; - } - - public int getItemCountIncludingAltered() { - return createdContent.gameData.items.size() + alteredContent.gameData.items.size() + baseContent.gameData.items.size(); - } - - public Item getItemIncludingAltered(int index) { - if (index < createdContent.gameData.items.size()) { - return createdContent.gameData.items.get(index); - } else if (index < createdContent.gameData.items.size() + alteredContent.gameData.items.size()) { - return alteredContent.gameData.items.get(index - createdContent.gameData.items.size()); - } else if (index < getItemCountIncludingAltered()) { - return baseContent.gameData.items.get(index - (createdContent.gameData.items.size() + alteredContent.gameData.items.size())); - } - return null; - } - - public int getItemIndex(Item item) { - if (item.getDataType() == GameSource.Type.created) { - return createdContent.gameData.items.getIndex(item); - } else { - return createdContent.gameData.items.size() + baseContent.gameData.items.indexOf(baseContent.gameData.getItem(item.id)); - } - } - - - public ItemCategory getItemCategory(String id) { - ItemCategory gde = createdContent.gameData.getItemCategory(id); - if (gde == null) gde = alteredContent.gameData.getItemCategory(id); - if (gde == null) gde = baseContent.gameData.getItemCategory(id); - return gde; - } - - public int getItemCategoryCount() { - return createdContent.gameData.itemCategories.size() + baseContent.gameData.itemCategories.size(); - } - - public ItemCategory getItemCategory(int index) { - if (index < createdContent.gameData.itemCategories.size()) { - return createdContent.gameData.itemCategories.get(index); - } else if (index < getItemCategoryCount()) { - return getItemCategory(baseContent.gameData.itemCategories.get(index - createdContent.gameData.itemCategories.size()).id); - } - return null; - } - - public int getItemCategoryIndex(ItemCategory iCat) { - if (iCat.getDataType() == GameSource.Type.created) { - return createdContent.gameData.itemCategories.getIndex(iCat); - } else { - return createdContent.gameData.itemCategories.size() + baseContent.gameData.itemCategories.indexOf(baseContent.gameData.getItemCategory(iCat.id)); - } - } - - - public NPC getNPC(String id) { - NPC gde = createdContent.gameData.getNPC(id); - if (gde == null) gde = alteredContent.gameData.getNPC(id); - if (gde == null) gde = baseContent.gameData.getNPC(id); - return gde; - } - - public NPC getNPCIgnoreCase(String id) { - NPC gde = createdContent.gameData.getNPCIgnoreCase(id); - if (gde == null) gde = alteredContent.gameData.getNPCIgnoreCase(id); - if (gde == null) gde = baseContent.gameData.getNPCIgnoreCase(id); - return gde; - } - - public int getNPCCount() { - return createdContent.gameData.npcs.size() + baseContent.gameData.npcs.size(); - } - - public NPC getNPC(int index) { - if (index < createdContent.gameData.npcs.size()) { - return createdContent.gameData.npcs.get(index); - } else if (index < getNPCCount()) { - return getNPC(baseContent.gameData.npcs.get(index - createdContent.gameData.npcs.size()).id); - } - return null; - } - - public int getNPCCountIncludingAltered() { - return createdContent.gameData.npcs.size() + alteredContent.gameData.npcs.size() + baseContent.gameData.npcs.size(); - } - - public NPC getNPCIncludingAltered(int index) { - if (index < createdContent.gameData.npcs.size()) { - return createdContent.gameData.npcs.get(index); - } else if (index < createdContent.gameData.npcs.size() + alteredContent.gameData.npcs.size()) { - return alteredContent.gameData.npcs.get(index - createdContent.gameData.npcs.size()); - } else if (index < getNPCCountIncludingAltered()) { - return baseContent.gameData.npcs.get(index - (createdContent.gameData.npcs.size() + alteredContent.gameData.npcs.size())); - } - return null; - } - - public int getNPCIndex(NPC npc) { - if (npc.getDataType() == GameSource.Type.created) { - return createdContent.gameData.npcs.getIndex(npc); - } else { - return createdContent.gameData.npcs.size() + baseContent.gameData.npcs.indexOf(baseContent.gameData.getNPC(npc.id)); - } - } - - - public Quest getQuest(String id) { - Quest gde = createdContent.gameData.getQuest(id); - if (gde == null) gde = alteredContent.gameData.getQuest(id); - if (gde == null) gde = baseContent.gameData.getQuest(id); - return gde; - } - - public int getQuestCount() { - return createdContent.gameData.quests.size() + baseContent.gameData.quests.size(); - } - - public Quest getQuest(int index) { - if (index < createdContent.gameData.quests.size()) { - return createdContent.gameData.quests.get(index); - } else if (index < getQuestCount()) { - return getQuest(baseContent.gameData.quests.get(index - createdContent.gameData.quests.size()).id); - } - return null; - } - - public int getQuestIndex(Quest quest) { - if (quest.getDataType() == GameSource.Type.created) { - return createdContent.gameData.quests.getIndex(quest); - } else { - return createdContent.gameData.quests.size() + baseContent.gameData.quests.indexOf(baseContent.gameData.getQuest(quest.id)); - } - } - - public WorldmapSegment getWorldmapSegment(String id) { - WorldmapSegment gde = createdContent.getWorldmapSegment(id); - if (gde == null) gde = alteredContent.getWorldmapSegment(id); - if (gde == null) gde = baseContent.getWorldmapSegment(id); - return gde; - } - - public int getWorldmapSegmentCount() { - return createdContent.worldmap.size() + baseContent.worldmap.size(); - } - - public WorldmapSegment getWorldmapSegment(int index) { - if (index < createdContent.worldmap.size()) { - return createdContent.worldmap.get(index); - } else if (index < getWorldmapSegmentCount()) { - return getWorldmapSegment(baseContent.worldmap.get(index - createdContent.worldmap.size()).id); - } - return null; - } - - public int getWorldmapSegmentIndex(WorldmapSegment segment) { - if (segment.getDataType() == GameSource.Type.created) { - return createdContent.worldmap.getIndex(segment); - } else { - return createdContent.worldmap.size() + baseContent.worldmap.indexOf(baseContent.getWorldmapSegment(segment.id)); - } - } - - public Image getIcon(String iconId) { - return baseContent.getIcon(iconId); - } - - public Image getImage(String iconId) { - return baseContent.getImage(iconId); - } - - public Spritesheet getSpritesheet(String id) { - Spritesheet sheet = createdContent.gameSprites.getSpritesheet(id); - if (sheet == null) sheet = alteredContent.gameSprites.getSpritesheet(id); - if (sheet == null) sheet = baseContent.gameSprites.getSpritesheet(id); - return sheet; - } - - public int getSpritesheetCount() { - return createdContent.gameSprites.spritesheets.size() + baseContent.gameSprites.spritesheets.size(); - } - - public Spritesheet getSpritesheet(int index) { - if (index < createdContent.gameSprites.spritesheets.size()) { - return createdContent.gameSprites.spritesheets.get(index); - } else if (index < getSpritesheetCount()) { - return getSpritesheet(baseContent.gameSprites.spritesheets.get(index - createdContent.gameSprites.spritesheets.size()).id); - } - return null; - } - - public int getSpritesheetIndex(Spritesheet spritesheet) { - if (spritesheet.getDataType() == GameSource.Type.created) { - return createdContent.gameSprites.spritesheets.indexOf(spritesheet); - } else { - return createdContent.gameSprites.spritesheets.size() + baseContent.gameSprites.spritesheets.indexOf(baseContent.gameSprites.getSpritesheet(spritesheet.id)); - } - } - - public TMXMap getMap(String id) { - TMXMap map = createdContent.gameMaps.getMap(id); - if (map == null) map = alteredContent.gameMaps.getMap(id); - if (map == null) map = baseContent.gameMaps.getMap(id); - return map; - } - - public int getMapCount() { - return createdContent.gameMaps.getChildCount() + baseContent.gameMaps.getChildCount(); - } - - public TMXMap getMap(int index) { - if (index < createdContent.gameMaps.getChildCount()) { - return createdContent.gameMaps.get(index); - } else if (index < getMapCount()) { - return getMap(baseContent.gameMaps.get(index - createdContent.gameMaps.getChildCount()).id); - } - return null; - } - - public int getMapIndex(TMXMap map) { - if (map.getDataType() == GameSource.Type.created) { - return createdContent.gameMaps.tmxMaps.indexOf(map); - } else { - return createdContent.gameMaps.tmxMaps.size() + baseContent.gameMaps.tmxMaps.indexOf(baseContent.gameMaps.getMap(map.id)); - } - } - - public int getWriterSketchCount() { - return createdContent.writerModeDataSet.getChildCount(); - } - - public WriterModeData getWriterSketch(String id) { - return createdContent.writerModeDataSet.getWriterSketch(id); - } - - public WriterModeData getWriterSketch(int index) { - if (index < createdContent.writerModeDataSet.getChildCount()) { - return createdContent.writerModeDataSet.get(index); - } - return null; - } - - @Override - public Project getProject() { - return this; - } - - @Override - public Image getIcon() { - return getOpenIcon(); - } - - @Override - public Image getClosedIcon() { - //TODO Create a cool Project icon. - return DefaultIcons.getStdClosedIcon(); - } - - @Override - public Image getLeafIcon() { - //TODO Create a cool Project icon. - return DefaultIcons.getStdClosedIcon(); - } - - @Override - public Image getOpenIcon() { - //TODO Create a cool Project icon. - return DefaultIcons.getStdOpenIcon(); - } - - public void makeWritable(JSONElement node) { - GameSource.Type type = node.getDataType(); - if (type == null) { - Notification.addError("Unable to make " + node.getDesc() + " writable. No owning GameDataSet found."); - } else { - if (type == GameSource.Type.source) { - JSONElement clone = (JSONElement) node.clone(); - if (node instanceof Quest) { - for (QuestStage oldStage : ((Quest) node).stages) { - QuestStage newStage = ((Quest) clone).getStage(oldStage.progress); - for (GameDataElement backlink : oldStage.getBacklinks()) { - backlink.elementChanged(oldStage, newStage); - } - oldStage.getBacklinks().clear(); - } - } - for (GameDataElement backlink : node.getBacklinks()) { - backlink.elementChanged(node, clone); - } - node.getBacklinks().clear(); - clone.writable = true; - clone.state = GameDataElement.State.created; - alteredContent.gameData.addElement(clone); - } else { - Notification.addError("Unable to make " + node.getDesc() + " writable. It does not originate from game source material."); - } - } - } - - - public void makeWritable(TMXMap node) { - GameSource.Type type = node.getDataType(); - if (type == null) { - Notification.addError("Unable to make " + node.getDesc() + " writable. No owning GameDataSet found."); - } else { - if (type == GameSource.Type.source) { - TMXMap clone = node.clone(); - for (GameDataElement backlink : node.getBacklinks()) { - backlink.elementChanged(node, clone); - } - node.getBacklinks().clear(); - clone.writable = true; - clone.state = GameDataElement.State.created; - alteredContent.gameMaps.addMap(clone); - } else { - Notification.addError("Unable to make " + node.getDesc() + " writable. It does not originate from game source material."); - } - } - } - - public void makeWritable(WorldmapSegment node) { - GameSource.Type type = node.getDataType(); - if (type == null) { - Notification.addError("Unable to make " + node.getDesc() + " writable. No owning GameDataSet found."); - } else { - if (type == GameSource.Type.source) { - WorldmapSegment clone = node.clone(); - for (GameDataElement backlink : node.getBacklinks()) { - backlink.elementChanged(node, clone); - } - clone.state = GameDataElement.State.init; - clone.parse(); - node.getBacklinks().clear(); - clone.writable = true; - clone.state = GameDataElement.State.created; - alteredContent.worldmap.addSegment(clone); - } else { - Notification.addError("Unable to make " + node.getDesc() + " writable. It does not originate from game source material."); - } - } - } - - /** - * @param node. Before calling this method, make sure that no other node with the same class and id exist in either created or altered. - */ - public void createElement(JSONElement node) { - node.writable = true; - if (getGameDataElement(node.getClass(), node.id) != null) { - GameDataElement existingNode = getGameDataElement(node.getClass(), node.id); - for (GameDataElement backlink : existingNode.getBacklinks()) { - backlink.elementChanged(existingNode, node); - } - existingNode.getBacklinks().clear(); - node.writable = true; - alteredContent.gameData.addElement(node); - node.link(); - node.state = GameDataElement.State.created; - } else { - createdContent.gameData.addElement(node); - node.link(); - node.state = GameDataElement.State.created; - } - fireElementAdded(node, getNodeIndex(node)); - } - - /** - * @param node. Before calling this method, make sure that no other node with the same class and id exist in either created or altered. - */ - public void createElements(List nodes) { - for (JSONElement node : nodes) { - //Already added. - if (node.getProject() != null) continue; - node.writable = true; - if (getGameDataElement(node.getClass(), node.id) != null) { - GameDataElement existingNode = getGameDataElement(node.getClass(), node.id); - for (GameDataElement backlink : existingNode.getBacklinks()) { - backlink.elementChanged(existingNode, node); - } - existingNode.getBacklinks().clear(); - node.writable = true; - alteredContent.gameData.addElement(node); - } else { - createdContent.gameData.addElement(node); - } - } - for (JSONElement node : nodes) { - node.link(); - node.state = GameDataElement.State.created; - fireElementAdded(node, getNodeIndex(node)); - } - } - - /** - * @param node. Before calling this method, make sure that no other map with the same id exist in either created or altered. - */ - public void createElement(TMXMap node) { - node.writable = true; - if (getMap(node.id) != null) { - GameDataElement existingNode = getMap(node.id); - for (GameDataElement backlink : existingNode.getBacklinks()) { - backlink.elementChanged(existingNode, node); - } - existingNode.getBacklinks().clear(); - node.writable = true; - node.tmxFile = new File(alteredContent.baseFolder, node.tmxFile.getName()); - node.parent = alteredContent.gameMaps; - alteredContent.gameMaps.addMap(node); - node.link(); - node.state = GameDataElement.State.created; - } else { - node.tmxFile = new File(createdContent.baseFolder, node.tmxFile.getName()); - node.parent = createdContent.gameMaps; - createdContent.gameMaps.addMap(node); - node.link(); - node.state = GameDataElement.State.created; - } - fireElementAdded(node, getNodeIndex(node)); - } - - - public void moveToCreated(JSONElement target) { - target.childrenRemoved(new ArrayList()); - ((GameDataCategory) target.getParent()).remove(target); - target.state = GameDataElement.State.created; - createdContent.gameData.addElement(target); - } - - public void moveToAltered(JSONElement target) { - target.childrenRemoved(new ArrayList()); - ((GameDataCategory) target.getParent()).remove(target); - target.state = GameDataElement.State.created; - ((JSONElement) target).jsonFile = new File(baseContent.gameData.getGameDataElement(((JSONElement) target).getClass(), target.id).jsonFile.getAbsolutePath()); - alteredContent.gameData.addElement((JSONElement) target); - } - - public void createWorldmapSegment(WorldmapSegment node) { - node.writable = true; - if (getWorldmapSegment(node.id) != null) { - WorldmapSegment existingNode = getWorldmapSegment(node.id); - for (GameDataElement backlink : existingNode.getBacklinks()) { - backlink.elementChanged(existingNode, node); - } - existingNode.getBacklinks().clear(); - node.writable = true; - node.state = GameDataElement.State.created; - alteredContent.worldmap.addSegment(node); - node.link(); - } else { - createdContent.worldmap.addSegment(node); - node.state = GameDataElement.State.created; - node.link(); - } - fireElementAdded(node, getNodeIndex(node)); - } - - - public void createWriterSketch(WriterModeData node) { - node.writable = true; - createdContent.writerModeDataSet.add(node); - node.link(); - fireElementAdded(node, getNodeIndex(node)); - } - - public void bookmark(GameDataElement gde) { - bookmarks.addBookmark(gde); - } - - - @Override - public GameDataSet getDataSet() { - return null; - } - - @Override - public Type getDataType() { - return null; - } - - - public String getSpritesheetsProperty(String string) { - return knownSpritesheetsProperties.getProperty(string); - } - - public void setSpritesheetsProperty(String key, String value) { - knownSpritesheetsProperties.setProperty(key, value); - } - - - @Override - public boolean isEmpty() { - return v.isEmpty(); - } - - - public void addSave(File selectedFile) { - saves.addSave(selectedFile); - } - - - public List getSpawnGroup(String spawngroup_id) { - List result = new ArrayList(); - int i = getNPCCount(); - boolean alreadyAdded = false; - int index = -1; - while (--i >= 0) { - NPC npc = getNPC(i); - if (spawngroup_id.equalsIgnoreCase(npc.spawngroup_id)) { - for (NPC present : result) { - if (present.id.equals(npc.id)) { - alreadyAdded = true; - index = result.indexOf(present); - break; - } - } - if (alreadyAdded) { - result.set(index, npc); - } else { - result.add(npc); - } - } - alreadyAdded = false; - } - if (result.isEmpty()) { - //Fallback case. A single NPC does not declare a spawn group, but is referred by its ID in maps' spawn areas. - NPC npc = getNPCIgnoreCase(spawngroup_id); - if (npc != null) result.add(npc); - } - return result; - } - - transient Map, List> projectElementListeners = new HashMap, List>(); - - public void addElementListener(Class interestingType, ProjectElementListener listener) { - if (projectElementListeners.get(interestingType) == null) { - projectElementListeners.put(interestingType, new ArrayList()); - } - projectElementListeners.get(interestingType).add(listener); - } - - public void removeElementListener(Class interestingType, ProjectElementListener listener) { - if (projectElementListeners.get(interestingType) != null) - projectElementListeners.get(interestingType).remove(listener); - } - - public void fireElementAdded(GameDataElement element, int index) { - if (projectElementListeners.get(element.getClass()) != null) { - for (ProjectElementListener l : projectElementListeners.get(element.getClass())) { - l.elementAdded(element, index); - } - } - } - - public void fireElementRemoved(GameDataElement element, int index) { - if (projectElementListeners.get(element.getClass()) != null) { - for (ProjectElementListener l : projectElementListeners.get(element.getClass())) { - l.elementRemoved(element, index); - } - } - } - - public void exportProjectAsZipPackage(final File target) { - WorkerDialog.showTaskMessage("Exporting project " + name + "...", ATContentStudio.frame, true, new Runnable() { - @Override - public void run() { - Notification.addInfo("Exporting project \"" + name + "\" as " + target.getAbsolutePath()); - - File tmpDir; - try { - tmpDir = exportProjectToTmpDir(); - FileUtils.writeToZip(tmpDir, target); - FileUtils.deleteDir(tmpDir); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - - Notification.addSuccess("Project \"" + name + "\" exported as " + target.getAbsolutePath()); - } - - - }); - } - - public void exportProjectOverGameSource(final File target) { - WorkerDialog.showTaskMessage("Exporting project " + name + "...", ATContentStudio.frame, true, new Runnable() { - @Override - public void run() { - Notification.addInfo("Exporting project \"" + name + "\" into " + target.getAbsolutePath()); - - File tmpDir; - try { - tmpDir = exportProjectToTmpDir(); - FileUtils.copyOver(tmpDir, target); - FileUtils.deleteDir(tmpDir); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - - Notification.addSuccess("Project \"" + name + "\" exported into " + target.getAbsolutePath()); - } - - - }); - } - - public File exportProjectToTmpDir() throws IOException { - File tmpDir = new File(baseFolder, "tmp"); - FileUtils.deleteDir(tmpDir); - tmpDir.mkdir(); - File tmpJsonDataDir = new File(tmpDir, GameDataSet.DEFAULT_REL_PATH_IN_SOURCE); - tmpJsonDataDir.mkdirs(); - -// for (File createdJsonFile : createdContent.gameData.baseFolder.listFiles()) { -// FileUtils.copyFile(createdJsonFile, new File(tmpJsonDataDir, createdJsonFile.getName())); -// } - Map, List> writtenFilesPerDataType = new LinkedHashMap, List>(); - List writtenFiles; - writtenFiles = writeDataDeltaForDataType(createdContent.gameData.actorConditions, alteredContent.gameData.actorConditions, baseContent.gameData.actorConditions, ActorCondition.class, - tmpJsonDataDir); - writtenFilesPerDataType.put(ActorCondition.class, writtenFiles); - writtenFiles = writeDataDeltaForDataType(createdContent.gameData.dialogues, alteredContent.gameData.dialogues, baseContent.gameData.dialogues, Dialogue.class, tmpJsonDataDir); - writtenFilesPerDataType.put(Dialogue.class, writtenFiles); - writtenFiles = writeDataDeltaForDataType(createdContent.gameData.droplists, alteredContent.gameData.droplists, baseContent.gameData.droplists, Droplist.class, tmpJsonDataDir); - writtenFilesPerDataType.put(Droplist.class, writtenFiles); - writtenFiles = writeDataDeltaForDataType(createdContent.gameData.itemCategories, alteredContent.gameData.itemCategories, baseContent.gameData.itemCategories, ItemCategory.class, - tmpJsonDataDir); - writtenFilesPerDataType.put(ItemCategory.class, writtenFiles); - writtenFiles = writeDataDeltaForDataType(createdContent.gameData.items, alteredContent.gameData.items, baseContent.gameData.items, Item.class, tmpJsonDataDir); - writtenFilesPerDataType.put(Item.class, writtenFiles); - writtenFiles = writeDataDeltaForDataType(createdContent.gameData.npcs, alteredContent.gameData.npcs, baseContent.gameData.npcs, NPC.class, tmpJsonDataDir); - writtenFilesPerDataType.put(NPC.class, writtenFiles); - writtenFiles = writeDataDeltaForDataType(createdContent.gameData.quests, alteredContent.gameData.quests, baseContent.gameData.quests, Quest.class, tmpJsonDataDir); - writtenFilesPerDataType.put(Quest.class, writtenFiles); - - File tmpMapDir = new File(tmpDir, TMXMapSet.DEFAULT_REL_PATH_IN_SOURCE); - tmpMapDir.mkdirs(); - writtenFiles = new LinkedList(); - for (File createdMapFile : createdContent.gameMaps.mapFolder.listFiles()) { - if (createdMapFile.getName().equalsIgnoreCase("worldmap.xml")) continue; - copyTmxConverted(createdMapFile.toPath(), Paths.get(tmpMapDir.getAbsolutePath(), createdMapFile.getName())); - writtenFiles.add(createdMapFile.getName()); - } - for (File alteredMapFile : alteredContent.gameMaps.mapFolder.listFiles()) { - if (alteredMapFile.getName().equalsIgnoreCase("worldmap.xml")) continue; - copyTmxConverted(alteredMapFile.toPath(), Paths.get(tmpMapDir.getAbsolutePath(), alteredMapFile.getName())); - writtenFiles.add(alteredMapFile.getName()); - } - writtenFilesPerDataType.put(TMXMap.class, writtenFiles); - - if (sourceSetToUse == ResourceSet.gameData) { - writeResourceListXml(writtenFilesPerDataType, GameSource.DEFAULT_REL_PATH_FOR_GAME_RESOURCE, baseContent.baseFolder, tmpDir); - } else if (sourceSetToUse == ResourceSet.debugData) { - writeResourceListXml(writtenFilesPerDataType, GameSource.DEFAULT_REL_PATH_FOR_DEBUG_RESOURCE, baseContent.baseFolder, tmpDir); - } - - - if (!createdContent.worldmap.isEmpty() || !alteredContent.worldmap.isEmpty()) { - try { - Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); - doc.setXmlVersion("1.0"); - Element root = doc.createElement("worldmap"); - doc.appendChild(root); - - for (int i = 0; i < getWorldmapSegmentCount(); i++) { - root.appendChild(getWorldmapSegment(i).toXmlElement(doc)); - } - - Worldmap.saveDocToFile(doc, new File(tmpMapDir, "worldmap.xml")); - } catch (ParserConfigurationException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - - return tmpDir; - } - - private void copyTmxConverted(Path from, Path to) throws IOException { - String xml = new String(Files.readAllBytes(from), StandardCharsets.UTF_8); - xml = xml.replace("../../altered/spritesheets/", drawablePath); - xml = xml.replace("../../created/spritesheets/", drawablePath); - xml = xml.replace("../spritesheets/", drawablePath); - xml = xml.replace("../spritesheets/", drawablePath); - Files.write(to, xml.getBytes(StandardCharsets.UTF_8)); - } - - - @SuppressWarnings("rawtypes") - public List writeDataDeltaForDataType(GameDataCategory created, GameDataCategory altered, GameDataCategory source, Class gdeClass, File targetFolder) { - List filenamesToWrite = new LinkedList(); - Map> dataToWritePerFilename = new LinkedHashMap>(); - for (JSONElement gde : altered) { - if (!filenamesToWrite.contains(gde.jsonFile.getName())) { - filenamesToWrite.add(gde.jsonFile.getName()); - } - } - for (JSONElement gde : created) { - if (!filenamesToWrite.contains(gde.jsonFile.getName())) { - filenamesToWrite.add(gde.jsonFile.getName()); - } - } - for (String fName : filenamesToWrite) { - for (JSONElement gde : source) { - if (gde.jsonFile.getName().equals(fName)) { - if (dataToWritePerFilename.get(fName) == null) { - dataToWritePerFilename.put(fName, new ArrayList()); - } - //Automatically fetches altered element over source element. - dataToWritePerFilename.get(fName).add(getGameDataElement(gdeClass, gde.id).toJson()); - } - } - for (JSONElement gde : created) { - if (gde.jsonFile.getName().equals(fName)) { - if (dataToWritePerFilename.get(fName) == null) { - dataToWritePerFilename.put(fName, new ArrayList()); - } - //Add the created elements. - dataToWritePerFilename.get(fName).add(getGameDataElement(gdeClass, gde.id).toJson()); - } - } - } - for (String fName : dataToWritePerFilename.keySet()) { - File jsonFile = new File(targetFolder, fName); - StringWriter writer = new JsonPrettyWriter(); - try { - JSONArray.writeJSONString(dataToWritePerFilename.get(fName), writer); - } catch (IOException e) { - //Impossible with a StringWriter - } - String textToWrite = writer.toString(); - try { - FileWriter w = new FileWriter(jsonFile); - w.write(textToWrite); - w.close(); -// Notification.addSuccess("Json file "+jsonFile.getAbsolutePath()+" saved."); - } catch (IOException e) { - Notification.addError("Error while writing json file " + jsonFile.getAbsolutePath() + " : " + e.getMessage()); - e.printStackTrace(); - } - } - return filenamesToWrite; - } - - - private void writeResourceListXml(Map, List> writtenFilesPerDataType, String xmlFileRelPath, File baseFolder, File tmpDir) { - File xmlFile = new File(baseFolder, xmlFileRelPath); - File outputFile = new File(tmpDir, xmlFileRelPath); - - Map> classNamesByArrayNames = new HashMap>(); - classNamesByArrayNames.put("loadresource_itemcategories", ItemCategory.class); - classNamesByArrayNames.put("loadresource_actorconditions", ActorCondition.class); - classNamesByArrayNames.put("loadresource_items", Item.class); - classNamesByArrayNames.put("loadresource_droplists", Droplist.class); - classNamesByArrayNames.put("loadresource_quests", Quest.class); - classNamesByArrayNames.put("loadresource_conversationlists", Dialogue.class); - classNamesByArrayNames.put("loadresource_monsters", NPC.class); - classNamesByArrayNames.put("loadresource_maps", TMXMap.class); - - String jsonResPrefix = "@raw/"; - String tmxResPrefix = "@xml/"; - String jsonFileSuffix = ".json"; - String tmxFileSuffix = ".tmx"; - - if (!xmlFile.exists()) return; - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - Document doc; - try { - factory.setIgnoringElementContentWhitespace(true); - factory.setExpandEntityReferences(false); - DocumentBuilder builder = factory.newDocumentBuilder(); - - InputSource insrc = new InputSource(new FileInputStream(xmlFile)); - insrc.setEncoding("UTF-8"); - doc = builder.parse(insrc); - - Element arrayNode; - String name, resPrefix, fileSuffix, resName, resToFile, fileToRes; - Class clazz; - List writtenFiles; - - Element root = (Element) doc.getElementsByTagName("resources").item(0); - if (root != null) { - NodeList arraysList = root.getElementsByTagName("array"); - if (arraysList != null) { - for (int i = 0; i < arraysList.getLength(); i++) { - arrayNode = (Element) arraysList.item(i); - name = arrayNode.getAttribute("name"); - clazz = classNamesByArrayNames.get(name); - if (clazz == null) continue; - writtenFiles = writtenFilesPerDataType.get(clazz); - if (writtenFiles == null) continue; - if (clazz == TMXMap.class) { - resPrefix = tmxResPrefix; - fileSuffix = tmxFileSuffix; - } else { - resPrefix = jsonResPrefix; - fileSuffix = jsonFileSuffix; - } - NodeList arrayItems = arrayNode.getElementsByTagName("item"); - if (arrayItems != null) { - for (int j = 0; j < arrayItems.getLength(); j++) { - resName = ((Element) arrayItems.item(j)).getTextContent(); - if (resName == null) continue; - resToFile = resName.replaceFirst("\\A" + resPrefix, "") + fileSuffix; - writtenFiles.remove(resToFile); - } - } - if (!writtenFiles.isEmpty()) { - Comment com = doc.createComment("Added by ATCS " + ATContentStudio.APP_VERSION + " for project " + getProject().name); - arrayNode.appendChild(com); - Collections.sort(writtenFiles); - for (String missingRes : writtenFiles) { - Element item = doc.createElement("item"); - fileToRes = resPrefix + missingRes.replaceFirst(fileSuffix + "\\z", ""); - item.setTextContent(fileToRes); - arrayNode.appendChild(item); - } - } - } - } - } - - Transformer transformer = TransformerFactory.newInstance().newTransformer(); - if (!outputFile.getParentFile().exists()) { - outputFile.getParentFile().mkdirs(); - } - StringWriter temp = new StringWriter(); - Result output = new StreamResult(temp); - Source input = new DOMSource(doc); - transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); - transformer.setOutputProperty(OutputKeys.METHOD, "xml"); - transformer.transform(input, output); - - String tempString = temp.toString(); - doc = builder.parse(new ByteArrayInputStream(tempString.getBytes("UTF-8"))); - input = new DOMSource(doc); - transformer = TransformerFactory.newInstance().newTransformer(new StreamSource(new StringReader( - "\r\n" + - "\r\n" + - " \r\n" + - " \r\n" + - "\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - "\r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - " \r\n" + - ""))); - output = new StreamResult(new FileOutputStream(outputFile)); - transformer.setOutputProperty(OutputKeys.INDENT, "yes"); - transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4"); - transformer.transform(input, output); - - - } catch (SAXException e) { - e.printStackTrace(); - } catch (FileNotFoundException e) { - e.printStackTrace(); - } catch (ParserConfigurationException e) { - e.printStackTrace(); - } catch (IOException e) { - e.printStackTrace(); - } catch (TransformerException e) { - e.printStackTrace(); - } - } - - @Override - public boolean needsSaving() { - for (ProjectTreeNode node : v.getNonEmptyIterable()) { - if (node.needsSaving()) return true; - } - return false; - } - - -} +package com.gpl.rpg.atcontentstudio.model; + +import com.gpl.rpg.atcontentstudio.ATContentStudio; +import com.gpl.rpg.atcontentstudio.Notification; +import com.gpl.rpg.atcontentstudio.io.JsonPrettyWriter; +import com.gpl.rpg.atcontentstudio.io.SettingsSave; +import com.gpl.rpg.atcontentstudio.model.GameSource.Type; +import com.gpl.rpg.atcontentstudio.model.bookmarks.BookmarksRoot; +import com.gpl.rpg.atcontentstudio.model.gamedata.*; +import com.gpl.rpg.atcontentstudio.model.maps.TMXMap; +import com.gpl.rpg.atcontentstudio.model.maps.TMXMapSet; +import com.gpl.rpg.atcontentstudio.model.maps.Worldmap; +import com.gpl.rpg.atcontentstudio.model.maps.WorldmapSegment; +import com.gpl.rpg.atcontentstudio.model.saves.SavedGamesSet; +import com.gpl.rpg.atcontentstudio.model.sprites.Spritesheet; +import com.gpl.rpg.atcontentstudio.model.tools.writermode.WriterModeData; +import com.gpl.rpg.atcontentstudio.ui.DefaultIcons; +import com.gpl.rpg.atcontentstudio.ui.WorkerDialog; +import com.gpl.rpg.atcontentstudio.utils.FileUtils; +import org.json.simple.JSONArray; +import org.w3c.dom.Comment; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +import javax.swing.tree.TreeNode; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.*; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import javax.xml.transform.stream.StreamSource; +import java.awt.*; +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import java.util.*; + +public class Project implements ProjectTreeNode, Serializable { + + private static final long serialVersionUID = 4807454973303366758L; + private static final String drawablePath = TMXMapSet.DEFAULT_REL_PATH_TO_DRAWABLE.replace("\\", "/"); + + + //Every instance field that is not transient will be saved in this file. + public static final String SETTINGS_FILE = ".project"; + + public String name; + + public File baseFolder; + public boolean open; + + public GameSource baseContent; //A.k.a library + + public GameSource referencedContent; //Pointers to base content + public transient GameSource alteredContent; //Copied from base content (does not overwrite yet) + public transient GameSource createdContent; //Stand-alone. + public transient BookmarksRoot bookmarks; + + + public SavedGamesSet saves; //For simulations. + + public transient SavedSlotCollection v; + + public transient Workspace parent; + + public Properties knownSpritesheetsProperties; + + public static enum ResourceSet { + gameData, + debugData, + allFiles + } + + public ResourceSet sourceSetToUse; + + public Project(Workspace w, String name, File source, ResourceSet sourceSet) { + this.parent = w; + this.name = name; + this.sourceSetToUse = sourceSet; + + //CREATE PROJECT + baseFolder = new File(w.baseFolder, name + File.separator); + try { + baseFolder.mkdir(); + } catch (SecurityException e) { + Notification.addError("Eror creating project root folder: " + e.getMessage()); + e.printStackTrace(); + } + open = true; + v = new SavedSlotCollection(); + + knownSpritesheetsProperties = new Properties(); + try { + knownSpritesheetsProperties.load(Project.class.getResourceAsStream("/spritesheets.properties")); + } catch (IOException e) { + Notification.addWarn("Unable to load default spritesheets properties."); + e.printStackTrace(); + } + + + baseContent = new GameSource(source, this); + +// referencedContent = new GameSource(this, GameSource.Type.referenced); + + alteredContent = new GameSource(this, GameSource.Type.altered); + createdContent = new GameSource(this, GameSource.Type.created); + bookmarks = new BookmarksRoot(this); + + saves = new SavedGamesSet(this); + + v.add(createdContent); + v.add(alteredContent); +// v.add(referencedContent); + v.add(baseContent); + v.add(saves); + v.add(bookmarks); + + linkAll(); + + save(); + } + + + @Override + public TreeNode getChildAt(int childIndex) { + return v.getNonEmptyElementAt(childIndex); + } + + @Override + public int getChildCount() { + return v.getNonEmptySize(); + } + + @Override + public TreeNode getParent() { + return parent; + } + + @Override + public int getIndex(TreeNode node) { + return v.getNonEmptyIndexOf((ProjectTreeNode) node); + } + + @Override + public boolean getAllowsChildren() { + return open; + } + + @Override + public boolean isLeaf() { + return !open; + } + + @Override + public Enumeration children() { + return v.getNonEmptyElements(); + } + + @Override + public void childrenAdded(List path) { + path.add(0, this); + parent.childrenAdded(path); + } + + @Override + public void childrenChanged(List path) { + path.add(0, this); + parent.childrenChanged(path); + } + + @Override + public void childrenRemoved(List path) { + path.add(0, this); + parent.childrenRemoved(path); + } + + @Override + public void notifyCreated() { + childrenAdded(new ArrayList()); + for (ProjectTreeNode node : v.getNonEmptyIterable()) { + node.notifyCreated(); + } + } + + @Override + public String getDesc() { + return (needsSaving() ? "*" : "") + name; + } + + + public void close() { + this.open = false; + childrenRemoved(new ArrayList()); + } + + public void open() { + open = true; + } + + + public static Project fromFolder(Workspace w, File projRoot) { + Project p; + File f = new File(projRoot, Project.SETTINGS_FILE); + if (!f.exists()) { + Notification.addError("Unable to find " + SETTINGS_FILE + " for project " + projRoot.getName()); + return null; + } else { + p = (Project) SettingsSave.loadInstance(f, "Project"); + } + p.refreshTransients(w); + return p; + } + + public void refreshTransients(Workspace w) { + this.parent = w; + + projectElementListeners = new HashMap, List>(); + + try { + knownSpritesheetsProperties = new Properties(); + knownSpritesheetsProperties.load(Project.class.getResourceAsStream("/spritesheets.properties")); + } catch (IOException e) { + Notification.addWarn("Unable to load default spritesheets properties."); + e.printStackTrace(); + } + + if (sourceSetToUse == null) { + sourceSetToUse = ResourceSet.allFiles; + } + +// long l = new Date().getTime(); + baseContent.refreshTransients(this); +// l = new Date().getTime() - l; +// System.out.println("All initialized in "+l+"ms."); +// referencedContent.refreshTransients(this); + alteredContent = new GameSource(this, GameSource.Type.altered); + createdContent = new GameSource(this, GameSource.Type.created); + bookmarks = new BookmarksRoot(this); + + saves.refreshTransients(); + + v = new SavedSlotCollection(); + v.add(createdContent); + v.add(alteredContent); +// v.add(referencedContent); + v.add(baseContent); + v.add(saves); + v.add(bookmarks); + + + linkAll(); + + } + + public void linkAll() { + for (ProjectTreeNode node : baseContent.gameData.v.getNonEmptyIterable()) { + if (node instanceof GameDataCategory) { + for (GameDataElement e : ((GameDataCategory) node)) { + e.link(); + } + } + } + for (ProjectTreeNode node : baseContent.gameMaps.tmxMaps) { + ((TMXMap) node).link(); + } + for (ProjectTreeNode node : alteredContent.gameData.v.getNonEmptyIterable()) { + if (node instanceof GameDataCategory) { + for (GameDataElement e : ((GameDataCategory) node)) { + e.link(); + } + } + } + for (ProjectTreeNode node : alteredContent.gameMaps.tmxMaps) { + ((TMXMap) node).link(); + } + for (ProjectTreeNode node : createdContent.gameData.v.getNonEmptyIterable()) { + if (node instanceof GameDataCategory) { + for (GameDataElement e : ((GameDataCategory) node)) { + e.link(); + } + } + } + for (ProjectTreeNode node : createdContent.gameMaps.tmxMaps) { + ((TMXMap) node).link(); + } + + for (WorldmapSegment node : createdContent.worldmap) { + node.link(); + } + for (WorldmapSegment node : alteredContent.worldmap) { + node.link(); + } + for (WorldmapSegment node : baseContent.worldmap) { + node.link(); + } + } + + public void save() { + SettingsSave.saveInstance(this, new File(baseFolder, Project.SETTINGS_FILE), "Project " + this.name); + } + + + public JSONElement getGameDataElement(Class gdeClass, String id) { + if (gdeClass == ActorCondition.class) { + return getActorCondition(id); + } + if (gdeClass == Dialogue.class) { + return getDialogue(id); + } + if (gdeClass == Droplist.class) { + return getDroplist(id); + } + if (gdeClass == ItemCategory.class) { + return getItemCategory(id); + } + if (gdeClass == Item.class) { + return getItem(id); + } + if (gdeClass == NPC.class) { + return getNPC(id); + } + if (gdeClass == Quest.class) { + return getQuest(id); + } + return null; + } + + public int getNodeCount(Class gdeClass) { + if (gdeClass == ActorCondition.class) { + return getActorConditionCount(); + } + if (gdeClass == Dialogue.class) { + return getDialogueCount(); + } + if (gdeClass == Droplist.class) { + return getDroplistCount(); + } + if (gdeClass == ItemCategory.class) { + return getItemCategoryCount(); + } + if (gdeClass == Item.class) { + return getItemCount(); + } + if (gdeClass == NPC.class) { + return getNPCCount(); + } + if (gdeClass == Quest.class) { + return getQuestCount(); + } + return 0; + } + + public int getNodeIndex(GameDataElement node) { + Class gdeClass = node.getClass(); + if (gdeClass == ActorCondition.class) { + return getActorConditionIndex((ActorCondition) node); + } + if (gdeClass == Dialogue.class) { + return getDialogueIndex((Dialogue) node); + } + if (gdeClass == Droplist.class) { + return getDroplistIndex((Droplist) node); + } + if (gdeClass == ItemCategory.class) { + return getItemCategoryIndex((ItemCategory) node); + } + if (gdeClass == Item.class) { + return getItemIndex((Item) node); + } + if (gdeClass == NPC.class) { + return getNPCIndex((NPC) node); + } + if (gdeClass == Quest.class) { + return getQuestIndex((Quest) node); + } + return 0; + } + + public ActorCondition getActorCondition(String id) { + ActorCondition gde = createdContent.gameData.getActorCondition(id); + if (gde == null) gde = alteredContent.gameData.getActorCondition(id); + if (gde == null) gde = baseContent.gameData.getActorCondition(id); + return gde; + } + + public int getActorConditionCount() { + return createdContent.gameData.actorConditions.size() + baseContent.gameData.actorConditions.size(); + } + + public ActorCondition getActorCondition(int index) { + if (index < createdContent.gameData.actorConditions.size()) { + return createdContent.gameData.actorConditions.get(index); + } else if (index < getActorConditionCount()) { + return getActorCondition(baseContent.gameData.actorConditions.get(index - createdContent.gameData.actorConditions.size()).id); + } + return null; + } + + public int getActorConditionIndex(ActorCondition ac) { + if (ac.getDataType() == GameSource.Type.created) { + return createdContent.gameData.actorConditions.getIndex(ac); + } else { + return createdContent.gameData.actorConditions.size() + baseContent.gameData.actorConditions.indexOf(baseContent.gameData.getActorCondition(ac.id)); + } + } + + + public Dialogue getDialogue(String id) { + Dialogue gde = createdContent.gameData.getDialogue(id); + if (gde == null) gde = alteredContent.gameData.getDialogue(id); + if (gde == null) gde = baseContent.gameData.getDialogue(id); + return gde; + } + + public int getDialogueCount() { + return createdContent.gameData.dialogues.size() + baseContent.gameData.dialogues.size(); + } + + public Dialogue getDialogue(int index) { + if (index < createdContent.gameData.dialogues.size()) { + return createdContent.gameData.dialogues.get(index); + } else if (index < getDialogueCount()) { + return getDialogue(baseContent.gameData.dialogues.get(index - createdContent.gameData.dialogues.size()).id); + } + return null; + } + + public int getDialogueIndex(Dialogue dialogue) { + if (dialogue.getDataType() == GameSource.Type.created) { + return createdContent.gameData.dialogues.getIndex(dialogue); + } else { + return createdContent.gameData.dialogues.size() + baseContent.gameData.dialogues.indexOf(baseContent.gameData.getDialogue(dialogue.id)); + } + } + + + public Droplist getDroplist(String id) { + Droplist gde = createdContent.gameData.getDroplist(id); + if (gde == null) gde = alteredContent.gameData.getDroplist(id); + if (gde == null) gde = baseContent.gameData.getDroplist(id); + return gde; + } + + public int getDroplistCount() { + return createdContent.gameData.droplists.size() + baseContent.gameData.droplists.size(); + } + + public Droplist getDroplist(int index) { + if (index < createdContent.gameData.droplists.size()) { + return createdContent.gameData.droplists.get(index); + } else if (index < getDroplistCount()) { + return getDroplist(baseContent.gameData.droplists.get(index - createdContent.gameData.droplists.size()).id); + } + return null; + } + + public int getDroplistIndex(Droplist droplist) { + if (droplist.getDataType() == GameSource.Type.created) { + return createdContent.gameData.droplists.getIndex(droplist); + } else { + return createdContent.gameData.droplists.size() + baseContent.gameData.droplists.indexOf(baseContent.gameData.getDroplist(droplist.id)); + } + } + + + public Item getItem(String id) { + Item gde = createdContent.gameData.getItem(id); + if (gde == null) gde = alteredContent.gameData.getItem(id); + if (gde == null) gde = baseContent.gameData.getItem(id); + return gde; + } + + public int getItemCount() { + return createdContent.gameData.items.size() + baseContent.gameData.items.size(); + } + + public Item getItem(int index) { + if (index < createdContent.gameData.items.size()) { + return createdContent.gameData.items.get(index); + } else if (index < getItemCount()) { + return getItem(baseContent.gameData.items.get(index - createdContent.gameData.items.size()).id); + } + return null; + } + + public int getItemCountIncludingAltered() { + return createdContent.gameData.items.size() + alteredContent.gameData.items.size() + baseContent.gameData.items.size(); + } + + public Item getItemIncludingAltered(int index) { + if (index < createdContent.gameData.items.size()) { + return createdContent.gameData.items.get(index); + } else if (index < createdContent.gameData.items.size() + alteredContent.gameData.items.size()) { + return alteredContent.gameData.items.get(index - createdContent.gameData.items.size()); + } else if (index < getItemCountIncludingAltered()) { + return baseContent.gameData.items.get(index - (createdContent.gameData.items.size() + alteredContent.gameData.items.size())); + } + return null; + } + + public int getItemIndex(Item item) { + if (item.getDataType() == GameSource.Type.created) { + return createdContent.gameData.items.getIndex(item); + } else { + return createdContent.gameData.items.size() + baseContent.gameData.items.indexOf(baseContent.gameData.getItem(item.id)); + } + } + + + public ItemCategory getItemCategory(String id) { + ItemCategory gde = createdContent.gameData.getItemCategory(id); + if (gde == null) gde = alteredContent.gameData.getItemCategory(id); + if (gde == null) gde = baseContent.gameData.getItemCategory(id); + return gde; + } + + public int getItemCategoryCount() { + return createdContent.gameData.itemCategories.size() + baseContent.gameData.itemCategories.size(); + } + + public ItemCategory getItemCategory(int index) { + if (index < createdContent.gameData.itemCategories.size()) { + return createdContent.gameData.itemCategories.get(index); + } else if (index < getItemCategoryCount()) { + return getItemCategory(baseContent.gameData.itemCategories.get(index - createdContent.gameData.itemCategories.size()).id); + } + return null; + } + + public int getItemCategoryIndex(ItemCategory iCat) { + if (iCat.getDataType() == GameSource.Type.created) { + return createdContent.gameData.itemCategories.getIndex(iCat); + } else { + return createdContent.gameData.itemCategories.size() + baseContent.gameData.itemCategories.indexOf(baseContent.gameData.getItemCategory(iCat.id)); + } + } + + + public NPC getNPC(String id) { + NPC gde = createdContent.gameData.getNPC(id); + if (gde == null) gde = alteredContent.gameData.getNPC(id); + if (gde == null) gde = baseContent.gameData.getNPC(id); + return gde; + } + + public NPC getNPCIgnoreCase(String id) { + NPC gde = createdContent.gameData.getNPCIgnoreCase(id); + if (gde == null) gde = alteredContent.gameData.getNPCIgnoreCase(id); + if (gde == null) gde = baseContent.gameData.getNPCIgnoreCase(id); + return gde; + } + + public int getNPCCount() { + return createdContent.gameData.npcs.size() + baseContent.gameData.npcs.size(); + } + + public NPC getNPC(int index) { + if (index < createdContent.gameData.npcs.size()) { + return createdContent.gameData.npcs.get(index); + } else if (index < getNPCCount()) { + return getNPC(baseContent.gameData.npcs.get(index - createdContent.gameData.npcs.size()).id); + } + return null; + } + + public int getNPCCountIncludingAltered() { + return createdContent.gameData.npcs.size() + alteredContent.gameData.npcs.size() + baseContent.gameData.npcs.size(); + } + + public NPC getNPCIncludingAltered(int index) { + if (index < createdContent.gameData.npcs.size()) { + return createdContent.gameData.npcs.get(index); + } else if (index < createdContent.gameData.npcs.size() + alteredContent.gameData.npcs.size()) { + return alteredContent.gameData.npcs.get(index - createdContent.gameData.npcs.size()); + } else if (index < getNPCCountIncludingAltered()) { + return baseContent.gameData.npcs.get(index - (createdContent.gameData.npcs.size() + alteredContent.gameData.npcs.size())); + } + return null; + } + + public int getNPCIndex(NPC npc) { + if (npc.getDataType() == GameSource.Type.created) { + return createdContent.gameData.npcs.getIndex(npc); + } else { + return createdContent.gameData.npcs.size() + baseContent.gameData.npcs.indexOf(baseContent.gameData.getNPC(npc.id)); + } + } + + + public Quest getQuest(String id) { + Quest gde = createdContent.gameData.getQuest(id); + if (gde == null) gde = alteredContent.gameData.getQuest(id); + if (gde == null) gde = baseContent.gameData.getQuest(id); + return gde; + } + + public int getQuestCount() { + return createdContent.gameData.quests.size() + baseContent.gameData.quests.size(); + } + + public Quest getQuest(int index) { + if (index < createdContent.gameData.quests.size()) { + return createdContent.gameData.quests.get(index); + } else if (index < getQuestCount()) { + return getQuest(baseContent.gameData.quests.get(index - createdContent.gameData.quests.size()).id); + } + return null; + } + + public int getQuestIndex(Quest quest) { + if (quest.getDataType() == GameSource.Type.created) { + return createdContent.gameData.quests.getIndex(quest); + } else { + return createdContent.gameData.quests.size() + baseContent.gameData.quests.indexOf(baseContent.gameData.getQuest(quest.id)); + } + } + + public WorldmapSegment getWorldmapSegment(String id) { + WorldmapSegment gde = createdContent.getWorldmapSegment(id); + if (gde == null) gde = alteredContent.getWorldmapSegment(id); + if (gde == null) gde = baseContent.getWorldmapSegment(id); + return gde; + } + + public int getWorldmapSegmentCount() { + return createdContent.worldmap.size() + baseContent.worldmap.size(); + } + + public WorldmapSegment getWorldmapSegment(int index) { + if (index < createdContent.worldmap.size()) { + return createdContent.worldmap.get(index); + } else if (index < getWorldmapSegmentCount()) { + return getWorldmapSegment(baseContent.worldmap.get(index - createdContent.worldmap.size()).id); + } + return null; + } + + public int getWorldmapSegmentIndex(WorldmapSegment segment) { + if (segment.getDataType() == GameSource.Type.created) { + return createdContent.worldmap.getIndex(segment); + } else { + return createdContent.worldmap.size() + baseContent.worldmap.indexOf(baseContent.getWorldmapSegment(segment.id)); + } + } + + public Image getIcon(String iconId) { + return baseContent.getIcon(iconId); + } + + public Image getImage(String iconId) { + return baseContent.getImage(iconId); + } + + public Spritesheet getSpritesheet(String id) { + Spritesheet sheet = createdContent.gameSprites.getSpritesheet(id); + if (sheet == null) sheet = alteredContent.gameSprites.getSpritesheet(id); + if (sheet == null) sheet = baseContent.gameSprites.getSpritesheet(id); + return sheet; + } + + public int getSpritesheetCount() { + return createdContent.gameSprites.spritesheets.size() + baseContent.gameSprites.spritesheets.size(); + } + + public Spritesheet getSpritesheet(int index) { + if (index < createdContent.gameSprites.spritesheets.size()) { + return createdContent.gameSprites.spritesheets.get(index); + } else if (index < getSpritesheetCount()) { + return getSpritesheet(baseContent.gameSprites.spritesheets.get(index - createdContent.gameSprites.spritesheets.size()).id); + } + return null; + } + + public int getSpritesheetIndex(Spritesheet spritesheet) { + if (spritesheet.getDataType() == GameSource.Type.created) { + return createdContent.gameSprites.spritesheets.indexOf(spritesheet); + } else { + return createdContent.gameSprites.spritesheets.size() + baseContent.gameSprites.spritesheets.indexOf(baseContent.gameSprites.getSpritesheet(spritesheet.id)); + } + } + + public TMXMap getMap(String id) { + TMXMap map = createdContent.gameMaps.getMap(id); + if (map == null) map = alteredContent.gameMaps.getMap(id); + if (map == null) map = baseContent.gameMaps.getMap(id); + return map; + } + + public int getMapCount() { + return createdContent.gameMaps.getChildCount() + baseContent.gameMaps.getChildCount(); + } + + public TMXMap getMap(int index) { + if (index < createdContent.gameMaps.getChildCount()) { + return createdContent.gameMaps.get(index); + } else if (index < getMapCount()) { + return getMap(baseContent.gameMaps.get(index - createdContent.gameMaps.getChildCount()).id); + } + return null; + } + + public int getMapIndex(TMXMap map) { + if (map.getDataType() == GameSource.Type.created) { + return createdContent.gameMaps.tmxMaps.indexOf(map); + } else { + return createdContent.gameMaps.tmxMaps.size() + baseContent.gameMaps.tmxMaps.indexOf(baseContent.gameMaps.getMap(map.id)); + } + } + + public int getWriterSketchCount() { + return createdContent.writerModeDataSet.getChildCount(); + } + + public WriterModeData getWriterSketch(String id) { + return createdContent.writerModeDataSet.getWriterSketch(id); + } + + public WriterModeData getWriterSketch(int index) { + if (index < createdContent.writerModeDataSet.getChildCount()) { + return createdContent.writerModeDataSet.get(index); + } + return null; + } + + @Override + public Project getProject() { + return this; + } + + @Override + public Image getIcon() { + return getOpenIcon(); + } + + @Override + public Image getClosedIcon() { + //TODO Create a cool Project icon. + return DefaultIcons.getStdClosedIcon(); + } + + @Override + public Image getLeafIcon() { + //TODO Create a cool Project icon. + return DefaultIcons.getStdClosedIcon(); + } + + @Override + public Image getOpenIcon() { + //TODO Create a cool Project icon. + return DefaultIcons.getStdOpenIcon(); + } + + public void makeWritable(JSONElement node) { + GameSource.Type type = node.getDataType(); + if (type == null) { + Notification.addError("Unable to make " + node.getDesc() + " writable. No owning GameDataSet found."); + } else { + if (type == GameSource.Type.source) { + JSONElement clone = (JSONElement) node.clone(); + if (node instanceof Quest) { + for (QuestStage oldStage : ((Quest) node).stages) { + QuestStage newStage = ((Quest) clone).getStage(oldStage.progress); + for (GameDataElement backlink : oldStage.getBacklinks()) { + backlink.elementChanged(oldStage, newStage); + } + oldStage.getBacklinks().clear(); + } + } + for (GameDataElement backlink : node.getBacklinks()) { + backlink.elementChanged(node, clone); + } + node.getBacklinks().clear(); + clone.writable = true; + clone.state = GameDataElement.State.created; + alteredContent.gameData.addElement(clone); + } else { + Notification.addError("Unable to make " + node.getDesc() + " writable. It does not originate from game source material."); + } + } + } + + + public void makeWritable(TMXMap node) { + GameSource.Type type = node.getDataType(); + if (type == null) { + Notification.addError("Unable to make " + node.getDesc() + " writable. No owning GameDataSet found."); + } else { + if (type == GameSource.Type.source) { + TMXMap clone = node.clone(); + for (GameDataElement backlink : node.getBacklinks()) { + backlink.elementChanged(node, clone); + } + node.getBacklinks().clear(); + clone.writable = true; + clone.state = GameDataElement.State.created; + alteredContent.gameMaps.addMap(clone); + } else { + Notification.addError("Unable to make " + node.getDesc() + " writable. It does not originate from game source material."); + } + } + } + + public void makeWritable(WorldmapSegment node) { + GameSource.Type type = node.getDataType(); + if (type == null) { + Notification.addError("Unable to make " + node.getDesc() + " writable. No owning GameDataSet found."); + } else { + if (type == GameSource.Type.source) { + WorldmapSegment clone = node.clone(); + for (GameDataElement backlink : node.getBacklinks()) { + backlink.elementChanged(node, clone); + } + clone.state = GameDataElement.State.init; + clone.parse(); + node.getBacklinks().clear(); + clone.writable = true; + clone.state = GameDataElement.State.created; + alteredContent.worldmap.addSegment(clone); + } else { + Notification.addError("Unable to make " + node.getDesc() + " writable. It does not originate from game source material."); + } + } + } + + /** + * @param node. Before calling this method, make sure that no other node with the same class and id exist in either created or altered. + */ + public void createElement(JSONElement node) { + node.writable = true; + if (getGameDataElement(node.getClass(), node.id) != null) { + GameDataElement existingNode = getGameDataElement(node.getClass(), node.id); + for (GameDataElement backlink : existingNode.getBacklinks()) { + backlink.elementChanged(existingNode, node); + } + existingNode.getBacklinks().clear(); + node.writable = true; + alteredContent.gameData.addElement(node); + node.link(); + node.state = GameDataElement.State.created; + } else { + createdContent.gameData.addElement(node); + node.link(); + node.state = GameDataElement.State.created; + } + fireElementAdded(node, getNodeIndex(node)); + } + + /** + * @param node. Before calling this method, make sure that no other node with the same class and id exist in either created or altered. + */ + public void createElements(List nodes) { + for (JSONElement node : nodes) { + //Already added. + if (node.getProject() != null) continue; + node.writable = true; + if (getGameDataElement(node.getClass(), node.id) != null) { + GameDataElement existingNode = getGameDataElement(node.getClass(), node.id); + for (GameDataElement backlink : existingNode.getBacklinks()) { + backlink.elementChanged(existingNode, node); + } + existingNode.getBacklinks().clear(); + node.writable = true; + alteredContent.gameData.addElement(node); + } else { + createdContent.gameData.addElement(node); + } + } + for (JSONElement node : nodes) { + node.link(); + node.state = GameDataElement.State.created; + fireElementAdded(node, getNodeIndex(node)); + } + } + + /** + * @param node. Before calling this method, make sure that no other map with the same id exist in either created or altered. + */ + public void createElement(TMXMap node) { + node.writable = true; + if (getMap(node.id) != null) { + GameDataElement existingNode = getMap(node.id); + for (GameDataElement backlink : existingNode.getBacklinks()) { + backlink.elementChanged(existingNode, node); + } + existingNode.getBacklinks().clear(); + node.writable = true; + node.tmxFile = new File(alteredContent.baseFolder, node.tmxFile.getName()); + node.parent = alteredContent.gameMaps; + alteredContent.gameMaps.addMap(node); + node.link(); + node.state = GameDataElement.State.created; + } else { + node.tmxFile = new File(createdContent.baseFolder, node.tmxFile.getName()); + node.parent = createdContent.gameMaps; + createdContent.gameMaps.addMap(node); + node.link(); + node.state = GameDataElement.State.created; + } + fireElementAdded(node, getNodeIndex(node)); + } + + + public void moveToCreated(JSONElement target) { + target.childrenRemoved(new ArrayList()); + ((GameDataCategory) target.getParent()).remove(target); + target.state = GameDataElement.State.created; + createdContent.gameData.addElement(target); + } + + public void moveToAltered(JSONElement target) { + target.childrenRemoved(new ArrayList()); + ((GameDataCategory) target.getParent()).remove(target); + target.state = GameDataElement.State.created; + ((JSONElement) target).jsonFile = new File(baseContent.gameData.getGameDataElement(((JSONElement) target).getClass(), target.id).jsonFile.getAbsolutePath()); + alteredContent.gameData.addElement((JSONElement) target); + } + + public void createWorldmapSegment(WorldmapSegment node) { + node.writable = true; + if (getWorldmapSegment(node.id) != null) { + WorldmapSegment existingNode = getWorldmapSegment(node.id); + for (GameDataElement backlink : existingNode.getBacklinks()) { + backlink.elementChanged(existingNode, node); + } + existingNode.getBacklinks().clear(); + node.writable = true; + node.state = GameDataElement.State.created; + alteredContent.worldmap.addSegment(node); + node.link(); + } else { + createdContent.worldmap.addSegment(node); + node.state = GameDataElement.State.created; + node.link(); + } + fireElementAdded(node, getNodeIndex(node)); + } + + + public void createWriterSketch(WriterModeData node) { + node.writable = true; + createdContent.writerModeDataSet.add(node); + node.link(); + fireElementAdded(node, getNodeIndex(node)); + } + + public void bookmark(GameDataElement gde) { + bookmarks.addBookmark(gde); + } + + + @Override + public GameDataSet getDataSet() { + return null; + } + + @Override + public Type getDataType() { + return null; + } + + + public String getSpritesheetsProperty(String string) { + return knownSpritesheetsProperties.getProperty(string); + } + + public void setSpritesheetsProperty(String key, String value) { + knownSpritesheetsProperties.setProperty(key, value); + } + + + @Override + public boolean isEmpty() { + return v.isEmpty(); + } + + + public void addSave(File selectedFile) { + saves.addSave(selectedFile); + } + + + public List getSpawnGroup(String spawngroup_id) { + List result = new ArrayList(); + int i = getNPCCount(); + boolean alreadyAdded = false; + int index = -1; + while (--i >= 0) { + NPC npc = getNPC(i); + if (spawngroup_id.equalsIgnoreCase(npc.spawngroup_id)) { + for (NPC present : result) { + if (present.id.equals(npc.id)) { + alreadyAdded = true; + index = result.indexOf(present); + break; + } + } + if (alreadyAdded) { + result.set(index, npc); + } else { + result.add(npc); + } + } + alreadyAdded = false; + } + if (result.isEmpty()) { + //Fallback case. A single NPC does not declare a spawn group, but is referred by its ID in maps' spawn areas. + NPC npc = getNPCIgnoreCase(spawngroup_id); + if (npc != null) result.add(npc); + } + return result; + } + + transient Map, List> projectElementListeners = new HashMap, List>(); + + public void addElementListener(Class interestingType, ProjectElementListener listener) { + if (projectElementListeners.get(interestingType) == null) { + projectElementListeners.put(interestingType, new ArrayList()); + } + projectElementListeners.get(interestingType).add(listener); + } + + public void removeElementListener(Class interestingType, ProjectElementListener listener) { + if (projectElementListeners.get(interestingType) != null) + projectElementListeners.get(interestingType).remove(listener); + } + + public void fireElementAdded(GameDataElement element, int index) { + if (projectElementListeners.get(element.getClass()) != null) { + for (ProjectElementListener l : projectElementListeners.get(element.getClass())) { + l.elementAdded(element, index); + } + } + } + + public void fireElementRemoved(GameDataElement element, int index) { + if (projectElementListeners.get(element.getClass()) != null) { + for (ProjectElementListener l : projectElementListeners.get(element.getClass())) { + l.elementRemoved(element, index); + } + } + } + + public void exportProjectAsZipPackage(final File target) { + WorkerDialog.showTaskMessage("Exporting project " + name + "...", ATContentStudio.frame, true, new Runnable() { + @Override + public void run() { + Notification.addInfo("Exporting project \"" + name + "\" as " + target.getAbsolutePath()); + + File tmpDir; + try { + tmpDir = exportProjectToTmpDir(); + FileUtils.writeToZip(tmpDir, target); + FileUtils.deleteDir(tmpDir); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + Notification.addSuccess("Project \"" + name + "\" exported as " + target.getAbsolutePath()); + } + + + }); + } + + public void exportProjectOverGameSource(final File target) { + WorkerDialog.showTaskMessage("Exporting project " + name + "...", ATContentStudio.frame, true, new Runnable() { + @Override + public void run() { + Notification.addInfo("Exporting project \"" + name + "\" into " + target.getAbsolutePath()); + + File tmpDir; + try { + tmpDir = exportProjectToTmpDir(); + FileUtils.copyOver(tmpDir, target); + FileUtils.deleteDir(tmpDir); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + Notification.addSuccess("Project \"" + name + "\" exported into " + target.getAbsolutePath()); + } + + + }); + } + + public File exportProjectToTmpDir() throws IOException { + File tmpDir = new File(baseFolder, "tmp"); + FileUtils.deleteDir(tmpDir); + tmpDir.mkdir(); + File tmpJsonDataDir = new File(tmpDir, GameDataSet.DEFAULT_REL_PATH_IN_SOURCE); + tmpJsonDataDir.mkdirs(); + +// for (File createdJsonFile : createdContent.gameData.baseFolder.listFiles()) { +// FileUtils.copyFile(createdJsonFile, new File(tmpJsonDataDir, createdJsonFile.getName())); +// } + Map, List> writtenFilesPerDataType = new LinkedHashMap, List>(); + List writtenFiles; + writtenFiles = writeDataDeltaForDataType(createdContent.gameData.actorConditions, alteredContent.gameData.actorConditions, baseContent.gameData.actorConditions, ActorCondition.class, + tmpJsonDataDir); + writtenFilesPerDataType.put(ActorCondition.class, writtenFiles); + writtenFiles = writeDataDeltaForDataType(createdContent.gameData.dialogues, alteredContent.gameData.dialogues, baseContent.gameData.dialogues, Dialogue.class, tmpJsonDataDir); + writtenFilesPerDataType.put(Dialogue.class, writtenFiles); + writtenFiles = writeDataDeltaForDataType(createdContent.gameData.droplists, alteredContent.gameData.droplists, baseContent.gameData.droplists, Droplist.class, tmpJsonDataDir); + writtenFilesPerDataType.put(Droplist.class, writtenFiles); + writtenFiles = writeDataDeltaForDataType(createdContent.gameData.itemCategories, alteredContent.gameData.itemCategories, baseContent.gameData.itemCategories, ItemCategory.class, + tmpJsonDataDir); + writtenFilesPerDataType.put(ItemCategory.class, writtenFiles); + writtenFiles = writeDataDeltaForDataType(createdContent.gameData.items, alteredContent.gameData.items, baseContent.gameData.items, Item.class, tmpJsonDataDir); + writtenFilesPerDataType.put(Item.class, writtenFiles); + writtenFiles = writeDataDeltaForDataType(createdContent.gameData.npcs, alteredContent.gameData.npcs, baseContent.gameData.npcs, NPC.class, tmpJsonDataDir); + writtenFilesPerDataType.put(NPC.class, writtenFiles); + writtenFiles = writeDataDeltaForDataType(createdContent.gameData.quests, alteredContent.gameData.quests, baseContent.gameData.quests, Quest.class, tmpJsonDataDir); + writtenFilesPerDataType.put(Quest.class, writtenFiles); + + File tmpMapDir = new File(tmpDir, TMXMapSet.DEFAULT_REL_PATH_IN_SOURCE); + tmpMapDir.mkdirs(); + writtenFiles = new LinkedList(); + for (File createdMapFile : createdContent.gameMaps.mapFolder.listFiles()) { + if (createdMapFile.getName().equalsIgnoreCase("worldmap.xml")) continue; + copyTmxConverted(createdMapFile.toPath(), Paths.get(tmpMapDir.getAbsolutePath(), createdMapFile.getName())); + writtenFiles.add(createdMapFile.getName()); + } + for (File alteredMapFile : alteredContent.gameMaps.mapFolder.listFiles()) { + if (alteredMapFile.getName().equalsIgnoreCase("worldmap.xml")) continue; + copyTmxConverted(alteredMapFile.toPath(), Paths.get(tmpMapDir.getAbsolutePath(), alteredMapFile.getName())); + writtenFiles.add(alteredMapFile.getName()); + } + writtenFilesPerDataType.put(TMXMap.class, writtenFiles); + + if (sourceSetToUse == ResourceSet.gameData) { + writeResourceListXml(writtenFilesPerDataType, GameSource.DEFAULT_REL_PATH_FOR_GAME_RESOURCE, baseContent.baseFolder, tmpDir); + } else if (sourceSetToUse == ResourceSet.debugData) { + writeResourceListXml(writtenFilesPerDataType, GameSource.DEFAULT_REL_PATH_FOR_DEBUG_RESOURCE, baseContent.baseFolder, tmpDir); + } + + + if (!createdContent.worldmap.isEmpty() || !alteredContent.worldmap.isEmpty()) { + try { + Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); + doc.setXmlVersion("1.0"); + Element root = doc.createElement("worldmap"); + doc.appendChild(root); + + for (int i = 0; i < getWorldmapSegmentCount(); i++) { + root.appendChild(getWorldmapSegment(i).toXmlElement(doc)); + } + + Worldmap.saveDocToFile(doc, new File(tmpMapDir, "worldmap.xml")); + } catch (ParserConfigurationException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + return tmpDir; + } + + private void copyTmxConverted(Path from, Path to) throws IOException { + String xml = new String(Files.readAllBytes(from), StandardCharsets.UTF_8); + xml = xml.replace("../../altered/spritesheets/", drawablePath); + xml = xml.replace("../../created/spritesheets/", drawablePath); + xml = xml.replace("../spritesheets/", drawablePath); + xml = xml.replace("../spritesheets/", drawablePath); + Files.write(to, xml.getBytes(StandardCharsets.UTF_8)); + } + + + @SuppressWarnings("rawtypes") + public List writeDataDeltaForDataType(GameDataCategory created, GameDataCategory altered, GameDataCategory source, Class gdeClass, File targetFolder) { + List filenamesToWrite = new LinkedList(); + Map> dataToWritePerFilename = new LinkedHashMap>(); + for (JSONElement gde : altered) { + if (!filenamesToWrite.contains(gde.jsonFile.getName())) { + filenamesToWrite.add(gde.jsonFile.getName()); + } + } + for (JSONElement gde : created) { + if (!filenamesToWrite.contains(gde.jsonFile.getName())) { + filenamesToWrite.add(gde.jsonFile.getName()); + } + } + for (String fName : filenamesToWrite) { + for (JSONElement gde : source) { + if (gde.jsonFile.getName().equals(fName)) { + if (dataToWritePerFilename.get(fName) == null) { + dataToWritePerFilename.put(fName, new ArrayList()); + } + //Automatically fetches altered element over source element. + dataToWritePerFilename.get(fName).add(getGameDataElement(gdeClass, gde.id).toJson()); + } + } + for (JSONElement gde : created) { + if (gde.jsonFile.getName().equals(fName)) { + if (dataToWritePerFilename.get(fName) == null) { + dataToWritePerFilename.put(fName, new ArrayList()); + } + //Add the created elements. + dataToWritePerFilename.get(fName).add(getGameDataElement(gdeClass, gde.id).toJson()); + } + } + } + for (String fName : dataToWritePerFilename.keySet()) { + File jsonFile = new File(targetFolder, fName); + + String textToWrite = FileUtils.toJsonString(dataToWritePerFilename.get(fName)); + FileUtils.writeStringToFile(textToWrite, jsonFile, "JSON file '"+jsonFile.getAbsolutePath()+"'", false); + } + return filenamesToWrite; + } + + + private void writeResourceListXml(Map, List> writtenFilesPerDataType, String xmlFileRelPath, File baseFolder, File tmpDir) { + File xmlFile = new File(baseFolder, xmlFileRelPath); + File outputFile = new File(tmpDir, xmlFileRelPath); + + Map> classNamesByArrayNames = new HashMap>(); + classNamesByArrayNames.put("loadresource_itemcategories", ItemCategory.class); + classNamesByArrayNames.put("loadresource_actorconditions", ActorCondition.class); + classNamesByArrayNames.put("loadresource_items", Item.class); + classNamesByArrayNames.put("loadresource_droplists", Droplist.class); + classNamesByArrayNames.put("loadresource_quests", Quest.class); + classNamesByArrayNames.put("loadresource_conversationlists", Dialogue.class); + classNamesByArrayNames.put("loadresource_monsters", NPC.class); + classNamesByArrayNames.put("loadresource_maps", TMXMap.class); + + String jsonResPrefix = "@raw/"; + String tmxResPrefix = "@xml/"; + String jsonFileSuffix = ".json"; + String tmxFileSuffix = ".tmx"; + + if (!xmlFile.exists()) return; + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + Document doc; + try { + factory.setIgnoringElementContentWhitespace(true); + factory.setExpandEntityReferences(false); + DocumentBuilder builder = factory.newDocumentBuilder(); + + InputSource insrc = new InputSource(new FileInputStream(xmlFile)); + insrc.setEncoding("UTF-8"); + doc = builder.parse(insrc); + + Element arrayNode; + String name, resPrefix, fileSuffix, resName, resToFile, fileToRes; + Class clazz; + List writtenFiles; + + Element root = (Element) doc.getElementsByTagName("resources").item(0); + if (root != null) { + NodeList arraysList = root.getElementsByTagName("array"); + if (arraysList != null) { + for (int i = 0; i < arraysList.getLength(); i++) { + arrayNode = (Element) arraysList.item(i); + name = arrayNode.getAttribute("name"); + clazz = classNamesByArrayNames.get(name); + if (clazz == null) continue; + writtenFiles = writtenFilesPerDataType.get(clazz); + if (writtenFiles == null) continue; + if (clazz == TMXMap.class) { + resPrefix = tmxResPrefix; + fileSuffix = tmxFileSuffix; + } else { + resPrefix = jsonResPrefix; + fileSuffix = jsonFileSuffix; + } + NodeList arrayItems = arrayNode.getElementsByTagName("item"); + if (arrayItems != null) { + for (int j = 0; j < arrayItems.getLength(); j++) { + resName = ((Element) arrayItems.item(j)).getTextContent(); + if (resName == null) continue; + resToFile = resName.replaceFirst("\\A" + resPrefix, "") + fileSuffix; + writtenFiles.remove(resToFile); + } + } + if (!writtenFiles.isEmpty()) { + Comment com = doc.createComment("Added by ATCS " + ATContentStudio.APP_VERSION + " for project " + getProject().name); + arrayNode.appendChild(com); + Collections.sort(writtenFiles); + for (String missingRes : writtenFiles) { + Element item = doc.createElement("item"); + fileToRes = resPrefix + missingRes.replaceFirst(fileSuffix + "\\z", ""); + item.setTextContent(fileToRes); + arrayNode.appendChild(item); + } + } + } + } + } + + Transformer transformer = TransformerFactory.newInstance().newTransformer(); + if (!outputFile.getParentFile().exists()) { + outputFile.getParentFile().mkdirs(); + } + StringWriter temp = new StringWriter(); + Result output = new StreamResult(temp); + Source input = new DOMSource(doc); + transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); + transformer.setOutputProperty(OutputKeys.METHOD, "xml"); + transformer.transform(input, output); + + String tempString = temp.toString(); + doc = builder.parse(new ByteArrayInputStream(tempString.getBytes("UTF-8"))); + input = new DOMSource(doc); + transformer = TransformerFactory.newInstance().newTransformer(new StreamSource(new StringReader( + "\r\n" + + "\r\n" + + " \r\n" + + " \r\n" + + "\r\n" + + " \r\n" + + " \r\n" + + " \r\n" + + " \r\n" + + " \r\n" + + "\r\n" + + " \r\n" + + " \r\n" + + " \r\n" + + " \r\n" + + ""))); + output = new StreamResult(new FileOutputStream(outputFile)); + transformer.setOutputProperty(OutputKeys.INDENT, "yes"); + transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4"); + transformer.transform(input, output); + + + } catch (SAXException e) { + e.printStackTrace(); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (ParserConfigurationException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } catch (TransformerException e) { + e.printStackTrace(); + } + } + + @Override + public boolean needsSaving() { + for (ProjectTreeNode node : v.getNonEmptyIterable()) { + if (node.needsSaving()) return true; + } + return false; + } + + +} diff --git a/src/com/gpl/rpg/atcontentstudio/model/WorkspaceSettings.java b/src/com/gpl/rpg/atcontentstudio/model/WorkspaceSettings.java index 37773e4..da026e3 100644 --- a/src/com/gpl/rpg/atcontentstudio/model/WorkspaceSettings.java +++ b/src/com/gpl/rpg/atcontentstudio/model/WorkspaceSettings.java @@ -1,8 +1,7 @@ package com.gpl.rpg.atcontentstudio.model; import com.gpl.rpg.atcontentstudio.Notification; -import com.gpl.rpg.atcontentstudio.io.JsonPrettyWriter; -import org.json.simple.JSONObject; +import com.gpl.rpg.atcontentstudio.utils.FileUtils; import org.json.simple.parser.JSONParser; import java.io.*; @@ -108,22 +107,8 @@ public class WorkspaceSettings { } json.put(VERSION_KEY, SETTINGS_VERSION); - StringWriter writer = new JsonPrettyWriter(); - try { - JSONObject.writeJSONString(json, writer); - } catch (IOException e) { - //Impossible with a StringWriter - } - String toWrite = writer.toString(); - try { - FileWriter w = new FileWriter(file); - w.write(toWrite); - w.close(); - Notification.addSuccess("Workspace settings saved."); - } catch (IOException e) { - Notification.addError("Error while saving workspace settings : " + e.getMessage()); - e.printStackTrace(); - } + String toWrite = FileUtils.toJsonString(json); + FileUtils.writeStringToFile(toWrite, file, "Workspace settings"); } public void resetDefault() { diff --git a/src/com/gpl/rpg/atcontentstudio/model/gamedata/GameDataCategory.java b/src/com/gpl/rpg/atcontentstudio/model/gamedata/GameDataCategory.java index 3b6b769..41bc63f 100644 --- a/src/com/gpl/rpg/atcontentstudio/model/gamedata/GameDataCategory.java +++ b/src/com/gpl/rpg/atcontentstudio/model/gamedata/GameDataCategory.java @@ -5,6 +5,7 @@ import com.gpl.rpg.atcontentstudio.io.JsonPrettyWriter; import com.gpl.rpg.atcontentstudio.model.*; import com.gpl.rpg.atcontentstudio.model.GameSource.Type; import com.gpl.rpg.atcontentstudio.ui.DefaultIcons; +import com.gpl.rpg.atcontentstudio.utils.FileUtils; import org.json.simple.JSONArray; import javax.swing.tree.TreeNode; @@ -157,26 +158,13 @@ public class GameDataCategory extends ArrayList implem return; } - 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(jsonFile); - w.write(toWrite); - w.close(); + + String toWrite = FileUtils.toJsonString(dataToSave); + if(FileUtils.writeStringToFile(toWrite, jsonFile, "JSON file '"+jsonFile.getAbsolutePath()+"'")){ for (E element : this) { element.state = GameDataElement.State.saved; } - Notification.addSuccess("Json file " + jsonFile.getAbsolutePath() + " saved."); - } catch (IOException e) { - Notification.addError("Error while writing json file " + jsonFile.getAbsolutePath() + " : " + e.getMessage()); - e.printStackTrace(); } - } diff --git a/src/com/gpl/rpg/atcontentstudio/model/gamedata/JSONElement.java b/src/com/gpl/rpg/atcontentstudio/model/gamedata/JSONElement.java index b8b1966..fec97da 100644 --- a/src/com/gpl/rpg/atcontentstudio/model/gamedata/JSONElement.java +++ b/src/com/gpl/rpg/atcontentstudio/model/gamedata/JSONElement.java @@ -4,6 +4,7 @@ import com.gpl.rpg.atcontentstudio.Notification; import com.gpl.rpg.atcontentstudio.io.JsonPrettyWriter; import com.gpl.rpg.atcontentstudio.model.GameDataElement; import com.gpl.rpg.atcontentstudio.model.SaveEvent; +import com.gpl.rpg.atcontentstudio.utils.FileUtils; import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; import org.json.simple.parser.ParseException; @@ -73,16 +74,10 @@ public abstract class JSONElement extends GameDataElement { public abstract Map toJson(); public String toJsonString() { - StringWriter writer = new JsonPrettyWriter(); - try { - JSONObject.writeJSONString(this.toJson(), writer); - } catch (IOException e) { - //Impossible with a StringWriter - } - return writer.toString(); + Map json = this.toJson(); + return FileUtils.toJsonString(json); } - @Override public GameDataSet getDataSet() { if (parent == null) { diff --git a/src/com/gpl/rpg/atcontentstudio/model/tools/resoptimizer/ResourcesCompactor.java b/src/com/gpl/rpg/atcontentstudio/model/tools/resoptimizer/ResourcesCompactor.java index 2ae6eb3..36a6bd1 100644 --- a/src/com/gpl/rpg/atcontentstudio/model/tools/resoptimizer/ResourcesCompactor.java +++ b/src/com/gpl/rpg/atcontentstudio/model/tools/resoptimizer/ResourcesCompactor.java @@ -145,16 +145,12 @@ public class ResourcesCompactor { 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(); + String toWrite = FileUtils.toJsonString(dataToSave); + toWrite = jsonMinifier.minify(toWrite); + FileUtils.writeStringToFile(toWrite, target, null); try { FileWriter w = new FileWriter(target); - w.write(jsonMinifier.minify(toWrite)); + w.write(toWrite); w.close(); } catch (IOException e) { e.printStackTrace(); diff --git a/src/com/gpl/rpg/atcontentstudio/model/tools/writermode/WriterModeDataSet.java b/src/com/gpl/rpg/atcontentstudio/model/tools/writermode/WriterModeDataSet.java index 11ba490..d9f5cb1 100644 --- a/src/com/gpl/rpg/atcontentstudio/model/tools/writermode/WriterModeDataSet.java +++ b/src/com/gpl/rpg/atcontentstudio/model/tools/writermode/WriterModeDataSet.java @@ -6,6 +6,7 @@ import com.gpl.rpg.atcontentstudio.model.*; import com.gpl.rpg.atcontentstudio.model.GameSource.Type; import com.gpl.rpg.atcontentstudio.model.gamedata.GameDataSet; import com.gpl.rpg.atcontentstudio.ui.DefaultIcons; +import com.gpl.rpg.atcontentstudio.utils.FileUtils; import org.json.simple.JSONArray; import org.json.simple.parser.JSONParser; import org.json.simple.parser.ParseException; @@ -155,24 +156,12 @@ public class WriterModeDataSet implements ProjectTreeNode, Serializable { return; } - 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(writerFile); - w.write(toWrite); - w.close(); + + String toWrite = FileUtils.toJsonString(dataToSave); + if(FileUtils.writeStringToFile(toWrite, writerFile, "Json file " + writerFile.getAbsolutePath())) { for (WriterModeData element : writerModeDataList) { element.state = GameDataElement.State.saved; } - Notification.addSuccess("Json file " + writerFile.getAbsolutePath() + " saved."); - } catch (IOException e) { - Notification.addError("Error while writing json file " + writerFile.getAbsolutePath() + " : " + e.getMessage()); - e.printStackTrace(); } } diff --git a/src/com/gpl/rpg/atcontentstudio/utils/FileUtils.java b/src/com/gpl/rpg/atcontentstudio/utils/FileUtils.java index 11959bd..f7e05d5 100644 --- a/src/com/gpl/rpg/atcontentstudio/utils/FileUtils.java +++ b/src/com/gpl/rpg/atcontentstudio/utils/FileUtils.java @@ -1,219 +1,266 @@ -package com.gpl.rpg.atcontentstudio.utils; - -import java.io.*; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.file.StandardCopyOption; -import java.util.zip.ZipEntry; -import java.util.zip.ZipOutputStream; - -public class FileUtils { - - public static void deleteDir(File dir) { - if (dir.exists()) { - for (File f : dir.listFiles()) { - if (f.isDirectory()) { - deleteDir(f); - } else { - f.delete(); - } - } - dir.delete(); - } - } - - public static void copyFile(File sourceLocation, File targetLocation) { - try { - InputStream in = new FileInputStream(sourceLocation); - OutputStream out = new FileOutputStream(targetLocation); - - // Copy the bits from instream to outstream - byte[] buf = new byte[1024]; - int len; - try { - while ((len = in.read(buf)) > 0) { - out.write(buf, 0, len); - } - } catch (IOException e) { - // TODO Auto-generated catch block - } finally { - try { - in.close(); - } catch (IOException e) { - // TODO Auto-generated catch block - } - try { - out.close(); - } catch (IOException e) { - // TODO Auto-generated catch block - } - } - } catch (FileNotFoundException e1) { - // TODO Auto-generated catch block - } - } - - private static final int BUFFER = 2048; - - public static void writeToZip(File folder, File target) { - try { - FileOutputStream dest = new FileOutputStream(target); - ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(dest)); - zipDir(folder, "", out); - out.flush(); - out.close(); - } catch (Exception e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - - } - - /** - * cp sourceFolder/* targetFolder/ - * - * @param sourceFolder - * @param targetFolder - */ - public static void copyOver(File sourceFolder, File targetFolder) { - if (!sourceFolder.isDirectory() || !targetFolder.isDirectory()) return; - for (File f : sourceFolder.listFiles()) { - if (Files.isSymbolicLink(f.toPath())) { - //Skip symlinks - continue; - } else if (f.isDirectory()) { - File dest = new File(targetFolder, f.getName()); - if (!dest.exists()) { - dest.mkdir(); - } - copyOver(f, dest); - } else { - copyFile(f, new File(targetFolder, f.getName())); - } - } - } - - private static void zipDir(File dir, String prefix, ZipOutputStream zos) { - if (prefix != "") { - prefix = prefix + File.separator; - } - for (File f : dir.listFiles()) { - if (f.isDirectory()) { - zipDir(f, prefix + f.getName(), zos); - } else { - FileInputStream fis; - try { - fis = new FileInputStream(f); - BufferedInputStream origin = new BufferedInputStream(fis, BUFFER); - ZipEntry entry = new ZipEntry(prefix + f.getName()); - try { - zos.putNextEntry(entry); - int count; - byte data[] = new byte[BUFFER]; - while ((count = origin.read(data, 0, BUFFER)) != -1) { - zos.write(data, 0, count); - zos.flush(); - } - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } finally { - try { - origin.close(); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - } catch (FileNotFoundException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - } - } - - public static boolean makeSymlink(File targetFile, File linkFile) { - Path target = Paths.get(targetFile.getAbsolutePath()); - Path link = Paths.get(linkFile.getAbsolutePath()); - if (!Files.exists(link)) { - try { - Files.createSymbolicLink(link, target); - } catch (Exception e) { - System.err.println("Failed to create symbolic link to target \"" + targetFile.getAbsolutePath() + "\" as \"" + linkFile.getAbsolutePath() + "\" the java.nio way:"); - e.printStackTrace(); - switch (DesktopIntegration.detectedOS) { - case Windows: - System.err.println("Trying the Windows way with mklink"); - try { - Runtime.getRuntime().exec( - "cmd.exe /C mklink " + (targetFile.isDirectory() ? "/J " : "") + "\"" + linkFile.getAbsolutePath() + "\" \"" + targetFile.getAbsolutePath() + "\""); - } catch (IOException e1) { - e1.printStackTrace(); - } - if (!linkFile.exists()) { - System.err.println("Attempting UAC elevation through VBS script."); - runWithUac("cmd.exe /C mklink " + (targetFile.isDirectory() ? "/J " : "") + "\"" + linkFile.getAbsolutePath() + "\" \"" + targetFile.getAbsolutePath() + "\"", 3, linkFile); - } - break; - case MacOS: - case NIX: - case Other: - System.err.println("Trying the unix way with ln -s"); - try { - Runtime.getRuntime().exec("ln -s " + targetFile.getAbsolutePath() + " " + linkFile.getAbsolutePath()); - } catch (IOException e1) { - e1.printStackTrace(); - } - break; - default: - System.out.println("Unrecognized OS. Please contact ATCS dev."); - break; - - } - } - } - if (!Files.exists(link)) { - System.err.println("Failed to create link \"" + linkFile.getAbsolutePath() + "\" targetting \"" + targetFile.getAbsolutePath() + "\""); - System.err.println("You can try running ATCS with administrative privileges once, or create the symbolic link manually."); - } - return true; - } - - public static File backupFile(File f) { - try { - Path returned = Files.copy(Paths.get(f.getAbsolutePath()), Paths.get(f.getAbsolutePath() + ".bak"), StandardCopyOption.REPLACE_EXISTING); - return returned.toFile(); - } catch (IOException e) { - e.printStackTrace(); - } - return null; - } - - static final String uacBatName = "ATCS_elevateWithUac.bat"; - - public static void runWithUac(String command, int tries, File checkExists) { - File tmpFolder = new File(System.getProperty("java.io.tmpdir")); - File batFile = new File(tmpFolder, uacBatName); - batFile.deleteOnExit(); - FileWriter writer; - try { - writer = new FileWriter(batFile, false); - writer.write( - "@echo Set objShell = CreateObject(\"Shell.Application\") > %temp%\\sudo.tmp.vbs\r\n" - + "@echo args = Right(\"%*\", (Len(\"%*\") - Len(\"%1\"))) >> %temp%\\sudo.tmp.vbs\r\n" - + "@echo objShell.ShellExecute \"%1\", args, \"\", \"runas\" >> %temp%\\sudo.tmp.vbs\r\n" - + "@cscript %temp%\\sudo.tmp.vbs\r\n" - + "del /f %temp%\\sudo.tmp.vbs\r\n"); - writer.close(); - while (!checkExists.exists() && tries-- > 0) { - Runtime.getRuntime().exec(new String[]{"cmd.exe", "/C", batFile.getAbsolutePath() + " " + command}); - } - } catch (IOException e) { - e.printStackTrace(); - } - - } - -} +package com.gpl.rpg.atcontentstudio.utils; + +import com.gpl.rpg.atcontentstudio.Notification; +import com.gpl.rpg.atcontentstudio.io.JsonPrettyWriter; +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; + +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.util.List; +import java.util.Map; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +public class FileUtils { + + public static String toJsonString(Map json) { + StringWriter writer = new JsonPrettyWriter(); + try { + JSONObject.writeJSONString(json, writer); + } catch (IOException e) { + //Impossible with a StringWriter + } + return writer.toString(); + } + public static String toJsonString(List json) { + StringWriter writer = new JsonPrettyWriter(); + try { + JSONArray.writeJSONString(json, writer); + } catch (IOException e) { + //Impossible with a StringWriter + } + return writer.toString(); + } + + public static boolean writeStringToFile(String toWrite, File file, String type) { + return writeStringToFile(toWrite, file, type, true); + } + public static boolean writeStringToFile(String toWrite, File file, String type, boolean notifyOnSuccess) { + try { + FileWriter w = new FileWriter(file); + w.write(toWrite); + w.close(); + if(type != null) { + Notification.addSuccess(type + " saved."); + } + return true; + } catch (IOException e) { + if(type != null) { + Notification.addError("Error while saving " + type + " : " + e.getMessage()); + } + e.printStackTrace(); + return false; + } + } + + public static void deleteDir(File dir) { + if (dir.exists()) { + for (File f : dir.listFiles()) { + if (f.isDirectory()) { + deleteDir(f); + } else { + f.delete(); + } + } + dir.delete(); + } + } + + public static void copyFile(File sourceLocation, File targetLocation) { + try { + InputStream in = new FileInputStream(sourceLocation); + OutputStream out = new FileOutputStream(targetLocation); + + // Copy the bits from instream to outstream + byte[] buf = new byte[1024]; + int len; + try { + while ((len = in.read(buf)) > 0) { + out.write(buf, 0, len); + } + } catch (IOException e) { + // TODO Auto-generated catch block + } finally { + try { + in.close(); + } catch (IOException e) { + // TODO Auto-generated catch block + } + try { + out.close(); + } catch (IOException e) { + // TODO Auto-generated catch block + } + } + } catch (FileNotFoundException e1) { + // TODO Auto-generated catch block + } + } + + private static final int BUFFER = 2048; + + public static void writeToZip(File folder, File target) { + try { + FileOutputStream dest = new FileOutputStream(target); + ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(dest)); + zipDir(folder, "", out); + out.flush(); + out.close(); + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + } + + /** + * cp sourceFolder/* targetFolder/ + * + * @param sourceFolder + * @param targetFolder + */ + public static void copyOver(File sourceFolder, File targetFolder) { + if (!sourceFolder.isDirectory() || !targetFolder.isDirectory()) return; + for (File f : sourceFolder.listFiles()) { + if (Files.isSymbolicLink(f.toPath())) { + //Skip symlinks + continue; + } else if (f.isDirectory()) { + File dest = new File(targetFolder, f.getName()); + if (!dest.exists()) { + dest.mkdir(); + } + copyOver(f, dest); + } else { + copyFile(f, new File(targetFolder, f.getName())); + } + } + } + + private static void zipDir(File dir, String prefix, ZipOutputStream zos) { + if (prefix != "") { + prefix = prefix + File.separator; + } + for (File f : dir.listFiles()) { + if (f.isDirectory()) { + zipDir(f, prefix + f.getName(), zos); + } else { + FileInputStream fis; + try { + fis = new FileInputStream(f); + BufferedInputStream origin = new BufferedInputStream(fis, BUFFER); + ZipEntry entry = new ZipEntry(prefix + f.getName()); + try { + zos.putNextEntry(entry); + int count; + byte data[] = new byte[BUFFER]; + while ((count = origin.read(data, 0, BUFFER)) != -1) { + zos.write(data, 0, count); + zos.flush(); + } + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } finally { + try { + origin.close(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + } catch (FileNotFoundException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + } + } + + public static boolean makeSymlink(File targetFile, File linkFile) { + Path target = Paths.get(targetFile.getAbsolutePath()); + Path link = Paths.get(linkFile.getAbsolutePath()); + if (!Files.exists(link)) { + try { + Files.createSymbolicLink(link, target); + } catch (Exception e) { + System.err.println("Failed to create symbolic link to target \"" + targetFile.getAbsolutePath() + "\" as \"" + linkFile.getAbsolutePath() + "\" the java.nio way:"); + e.printStackTrace(); + switch (DesktopIntegration.detectedOS) { + case Windows: + System.err.println("Trying the Windows way with mklink"); + try { + Runtime.getRuntime().exec( + "cmd.exe /C mklink " + (targetFile.isDirectory() ? "/J " : "") + "\"" + linkFile.getAbsolutePath() + "\" \"" + targetFile.getAbsolutePath() + "\""); + } catch (IOException e1) { + e1.printStackTrace(); + } + if (!linkFile.exists()) { + System.err.println("Attempting UAC elevation through VBS script."); + runWithUac("cmd.exe /C mklink " + (targetFile.isDirectory() ? "/J " : "") + "\"" + linkFile.getAbsolutePath() + "\" \"" + targetFile.getAbsolutePath() + "\"", 3, linkFile); + } + break; + case MacOS: + case NIX: + case Other: + System.err.println("Trying the unix way with ln -s"); + try { + Runtime.getRuntime().exec("ln -s " + targetFile.getAbsolutePath() + " " + linkFile.getAbsolutePath()); + } catch (IOException e1) { + e1.printStackTrace(); + } + break; + default: + System.out.println("Unrecognized OS. Please contact ATCS dev."); + break; + + } + } + } + if (!Files.exists(link)) { + System.err.println("Failed to create link \"" + linkFile.getAbsolutePath() + "\" targetting \"" + targetFile.getAbsolutePath() + "\""); + System.err.println("You can try running ATCS with administrative privileges once, or create the symbolic link manually."); + } + return true; + } + + public static File backupFile(File f) { + try { + Path returned = Files.copy(Paths.get(f.getAbsolutePath()), Paths.get(f.getAbsolutePath() + ".bak"), StandardCopyOption.REPLACE_EXISTING); + return returned.toFile(); + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } + + static final String uacBatName = "ATCS_elevateWithUac.bat"; + + public static void runWithUac(String command, int tries, File checkExists) { + File tmpFolder = new File(System.getProperty("java.io.tmpdir")); + File batFile = new File(tmpFolder, uacBatName); + batFile.deleteOnExit(); + FileWriter writer; + try { + writer = new FileWriter(batFile, false); + writer.write( + "@echo Set objShell = CreateObject(\"Shell.Application\") > %temp%\\sudo.tmp.vbs\r\n" + + "@echo args = Right(\"%*\", (Len(\"%*\") - Len(\"%1\"))) >> %temp%\\sudo.tmp.vbs\r\n" + + "@echo objShell.ShellExecute \"%1\", args, \"\", \"runas\" >> %temp%\\sudo.tmp.vbs\r\n" + + "@cscript %temp%\\sudo.tmp.vbs\r\n" + + "del /f %temp%\\sudo.tmp.vbs\r\n"); + writer.close(); + while (!checkExists.exists() && tries-- > 0) { + Runtime.getRuntime().exec(new String[]{"cmd.exe", "/C", batFile.getAbsolutePath() + " " + command}); + } + } catch (IOException e) { + e.printStackTrace(); + } + + } + +} From 7929ffe2a7abcf15e07f96bf2fd1c801b0635dcc Mon Sep 17 00:00:00 2001 From: OMGeeky Date: Tue, 24 Jun 2025 21:51:41 +0200 Subject: [PATCH 02/15] remove saves (temporarily?) since they don't really work --- src/com/gpl/rpg/atcontentstudio/model/Project.java | 12 ++++++------ src/com/gpl/rpg/atcontentstudio/utils/FileUtils.java | 4 ++++ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/com/gpl/rpg/atcontentstudio/model/Project.java b/src/com/gpl/rpg/atcontentstudio/model/Project.java index 57c24a9..5363a1b 100644 --- a/src/com/gpl/rpg/atcontentstudio/model/Project.java +++ b/src/com/gpl/rpg/atcontentstudio/model/Project.java @@ -64,7 +64,7 @@ public class Project implements ProjectTreeNode, Serializable { public transient BookmarksRoot bookmarks; - public SavedGamesSet saves; //For simulations. +// public SavedGamesSet saves; //For simulations. public transient SavedSlotCollection v; @@ -113,13 +113,13 @@ public class Project implements ProjectTreeNode, Serializable { createdContent = new GameSource(this, GameSource.Type.created); bookmarks = new BookmarksRoot(this); - saves = new SavedGamesSet(this); +// saves = new SavedGamesSet(this); v.add(createdContent); v.add(alteredContent); // v.add(referencedContent); v.add(baseContent); - v.add(saves); +// v.add(saves); v.add(bookmarks); linkAll(); @@ -244,14 +244,14 @@ public class Project implements ProjectTreeNode, Serializable { createdContent = new GameSource(this, GameSource.Type.created); bookmarks = new BookmarksRoot(this); - saves.refreshTransients(); +// saves.refreshTransients(); v = new SavedSlotCollection(); v.add(createdContent); v.add(alteredContent); // v.add(referencedContent); v.add(baseContent); - v.add(saves); +// v.add(saves); v.add(bookmarks); @@ -982,7 +982,7 @@ public class Project implements ProjectTreeNode, Serializable { public void addSave(File selectedFile) { - saves.addSave(selectedFile); +// saves.addSave(selectedFile); } diff --git a/src/com/gpl/rpg/atcontentstudio/utils/FileUtils.java b/src/com/gpl/rpg/atcontentstudio/utils/FileUtils.java index f7e05d5..4797b77 100644 --- a/src/com/gpl/rpg/atcontentstudio/utils/FileUtils.java +++ b/src/com/gpl/rpg/atcontentstudio/utils/FileUtils.java @@ -2,6 +2,7 @@ package com.gpl.rpg.atcontentstudio.utils; import com.gpl.rpg.atcontentstudio.Notification; import com.gpl.rpg.atcontentstudio.io.JsonPrettyWriter; +import com.gpl.rpg.atcontentstudio.io.JsonSerializable; import org.json.simple.JSONArray; import org.json.simple.JSONObject; @@ -17,6 +18,9 @@ import java.util.zip.ZipOutputStream; public class FileUtils { + public static String toJsonString(JsonSerializable jsonSerializable) { + return toJsonString(jsonSerializable.toMap()); + } public static String toJsonString(Map json) { StringWriter writer = new JsonPrettyWriter(); try { From c43b8464a27ffb0254bdef63cfcd9d1243b8539e Mon Sep 17 00:00:00 2001 From: OMGeeky Date: Tue, 24 Jun 2025 23:27:38 +0200 Subject: [PATCH 03/15] save .workspace as json (under .workspace.json) falls back on old .workspace implementation --- .../atcontentstudio/io/JsonSerializable.java | 8 ++ .../atcontentstudio/model/Preferences.java | 38 +++++++- .../rpg/atcontentstudio/model/Workspace.java | 95 +++++++++++++++---- .../rpg/atcontentstudio/utils/FileUtils.java | 31 ++++++ 4 files changed, 154 insertions(+), 18 deletions(-) create mode 100644 src/com/gpl/rpg/atcontentstudio/io/JsonSerializable.java diff --git a/src/com/gpl/rpg/atcontentstudio/io/JsonSerializable.java b/src/com/gpl/rpg/atcontentstudio/io/JsonSerializable.java new file mode 100644 index 0000000..cd251a0 --- /dev/null +++ b/src/com/gpl/rpg/atcontentstudio/io/JsonSerializable.java @@ -0,0 +1,8 @@ +package com.gpl.rpg.atcontentstudio.io; + +import java.util.Map; + +public interface JsonSerializable { + Map toMap(); + void fromMap(Map map); +} diff --git a/src/com/gpl/rpg/atcontentstudio/model/Preferences.java b/src/com/gpl/rpg/atcontentstudio/model/Preferences.java index e43c2c0..e8ca1e6 100644 --- a/src/com/gpl/rpg/atcontentstudio/model/Preferences.java +++ b/src/com/gpl/rpg/atcontentstudio/model/Preferences.java @@ -1,19 +1,53 @@ package com.gpl.rpg.atcontentstudio.model; +import com.gpl.rpg.atcontentstudio.io.JsonSerializable; + import java.awt.*; import java.io.Serializable; import java.util.HashMap; import java.util.Map; -public class Preferences implements Serializable { +public class Preferences implements Serializable, JsonSerializable { private static final long serialVersionUID = 2455802658424031276L; public Dimension windowSize = null; - public Map splittersPositions = new HashMap(); + public Map splittersPositions = new HashMap<>(); public Preferences() { } + @Override + public Map toMap() { + Map map = new HashMap(); + + if(windowSize!= null){ + Map windowSizeMap = new HashMap<>(); + windowSizeMap.put("width", windowSize.width); + windowSizeMap.put("height", windowSize.height); + map.put("windowSize", windowSizeMap); + } + + map.put("splittersPositions", splittersPositions); + return map; + } + + @Override + public void fromMap(Map map) { + if(map == null) return; + + Map windowSize1 = (Map) map.get("windowSize"); + if(windowSize1 != null){ + windowSize = new Dimension(((Number) windowSize1.get("width")).intValue(), ((Number) windowSize1.get("height")).intValue()); + } + + Map splitters = (Map) map.get("splittersPositions"); + Map splittersInt = new HashMap<>(splitters.size()); + for (Map.Entry entry : splitters. entrySet()){ + splittersInt.put(entry.getKey(), entry.getValue().intValue()); + } + splittersPositions = splittersInt; + + } } diff --git a/src/com/gpl/rpg/atcontentstudio/model/Workspace.java b/src/com/gpl/rpg/atcontentstudio/model/Workspace.java index 1dcae03..10556e6 100644 --- a/src/com/gpl/rpg/atcontentstudio/model/Workspace.java +++ b/src/com/gpl/rpg/atcontentstudio/model/Workspace.java @@ -2,27 +2,29 @@ package com.gpl.rpg.atcontentstudio.model; import com.gpl.rpg.atcontentstudio.ATContentStudio; import com.gpl.rpg.atcontentstudio.Notification; +import com.gpl.rpg.atcontentstudio.io.JsonSerializable; import com.gpl.rpg.atcontentstudio.io.SettingsSave; import com.gpl.rpg.atcontentstudio.model.GameSource.Type; import com.gpl.rpg.atcontentstudio.model.gamedata.GameDataSet; import com.gpl.rpg.atcontentstudio.ui.ProjectsTree.ProjectsTreeModel; import com.gpl.rpg.atcontentstudio.ui.WorkerDialog; +import com.gpl.rpg.atcontentstudio.utils.FileUtils; +import org.jsoup.SerializationException; import javax.swing.tree.TreeNode; import javax.swing.tree.TreePath; import java.awt.*; -import java.io.File; -import java.io.IOException; -import java.io.Serializable; +import java.io.*; import java.nio.file.Files; import java.util.List; import java.util.*; -public class Workspace implements ProjectTreeNode, Serializable { +public class Workspace implements ProjectTreeNode, Serializable, JsonSerializable { private static final long serialVersionUID = 7938633033601384956L; public static final String WS_SETTINGS_FILE = ".workspace"; + public static final String WS_SETTINGS_FILE_JSON = ".workspace.json"; public static Workspace activeWorkspace; @@ -38,6 +40,7 @@ public class Workspace implements ProjectTreeNode, Serializable { public transient ProjectsTreeModel projectsTreeModel = null; public Workspace(File workspaceRoot) { + boolean freshWorkspace = false; baseFolder = workspaceRoot; if (!workspaceRoot.exists()) { try { @@ -49,44 +52,105 @@ public class Workspace implements ProjectTreeNode, Serializable { } } settings = new WorkspaceSettings(this); - settingsFile = new File(workspaceRoot, WS_SETTINGS_FILE); + settingsFile = new File(workspaceRoot, WS_SETTINGS_FILE_JSON); if (!settingsFile.exists()) { try { settingsFile.createNewFile(); + freshWorkspace = true; } catch (IOException e) { Notification.addError("Error creating workspace datafile: " + e.getMessage()); e.printStackTrace(); } + Notification.addSuccess("New workspace created: " + + workspaceRoot.getAbsolutePath()); } - Notification.addSuccess("New workspace created: " - + workspaceRoot.getAbsolutePath()); - save(); + if (freshWorkspace) + save(); + } + + @Override + public Map toMap() { + Map map = new HashMap(); + map.put("serialVersionUID", serialVersionUID); + map.put("preferences", preferences.toMap()); + map.put("projectsName", (new ArrayList(projectsName))); + map.put("projectsOpenByName", projectsOpenByName); + List l = new ArrayList<>(knownMapSourcesFolders.size()); + for (File f: knownMapSourcesFolders){ + l.add(f.getPath()); + } + map.put("knownMapSourcesFolders", l); + return map; + } + + @Override + public void fromMap(Map map) { + if(serialVersionUID != (long) map.get("serialVersionUID")){ + throw new SerializationException("wrong seriaVersionUID"); + } + + preferences.fromMap((Map) map.get("preferences")); + + projectsName = new HashSet<>((List) map.getOrDefault("projectsName", new HashSet())); + + projectsOpenByName = (Map) map.getOrDefault("projectsOpenByName", new HashMap() ); + + List knownMapSourcesFolders1 = (List) map.getOrDefault("knownMapSourcesFolders", new ArrayList()); + knownMapSourcesFolders = new HashSet<>(); + if (knownMapSourcesFolders1 != null){ + int size = knownMapSourcesFolders1.size(); + for (String path: knownMapSourcesFolders1) { + //TODO: catch invalid paths...? + knownMapSourcesFolders.add(new File(path)); + } + } + } public static void setActive(File workspaceRoot) { Workspace w; - File f = new File(workspaceRoot, WS_SETTINGS_FILE); - if (!workspaceRoot.exists() || !f.exists()) { - w = new Workspace(workspaceRoot); + File f2 = new File(workspaceRoot, WS_SETTINGS_FILE_JSON); + if (workspaceRoot.exists() && f2.exists()) { + w = loadWorkspaceFromJson(workspaceRoot, f2); + w.refreshTransients(); } else { - w = (Workspace) SettingsSave.loadInstance(f, "Workspace"); - if (w == null) { + Notification.addInfo("Could not find json workspace file. Falling back to binary"); + File f = new File(workspaceRoot, WS_SETTINGS_FILE); + if (!workspaceRoot.exists() || !f.exists()) { w = new Workspace(workspaceRoot); } else { - w.refreshTransients(); + w = (Workspace) SettingsSave.loadInstance(f, "Workspace"); + if (w == null) { + w = new Workspace(workspaceRoot); + w.save(); + } else { + w.refreshTransients(); + } } } activeWorkspace = w; } + private static Workspace loadWorkspaceFromJson(File workspaceRoot, File settingsFile) { + Workspace w = w = new Workspace(workspaceRoot); + String json = FileUtils.readFileToString(settingsFile); + if (json!= null) { + Map map = (Map)FileUtils.fromJsonString(json); + if(map!= null){ + w.fromMap(map); + } + } + return w; + } + public static void saveActive() { activeWorkspace.save(); } public void save() { settings.save(); - SettingsSave.saveInstance(this, settingsFile, "Workspace"); + FileUtils.writeStringToFile(FileUtils.toJsonString(this), settingsFile, "Workspace"); } @Override @@ -356,5 +420,4 @@ public class Workspace implements ProjectTreeNode, Serializable { } return false; } - } diff --git a/src/com/gpl/rpg/atcontentstudio/utils/FileUtils.java b/src/com/gpl/rpg/atcontentstudio/utils/FileUtils.java index 4797b77..ef74f57 100644 --- a/src/com/gpl/rpg/atcontentstudio/utils/FileUtils.java +++ b/src/com/gpl/rpg/atcontentstudio/utils/FileUtils.java @@ -5,6 +5,8 @@ import com.gpl.rpg.atcontentstudio.io.JsonPrettyWriter; import com.gpl.rpg.atcontentstudio.io.JsonSerializable; import org.json.simple.JSONArray; import org.json.simple.JSONObject; +import org.json.simple.parser.JSONParser; +import org.json.simple.parser.ParseException; import java.io.*; import java.nio.file.Files; @@ -40,6 +42,17 @@ public class FileUtils { return writer.toString(); } + public static Object fromJsonString(String json) { + Object o; + try { + JSONParser parser = new JSONParser(); + o = parser.parse(json); + } catch (ParseException e) { + throw new RuntimeException(e); + } + return o; + } + public static boolean writeStringToFile(String toWrite, File file, String type) { return writeStringToFile(toWrite, file, type, true); } @@ -61,6 +74,24 @@ public class FileUtils { } } + public static String readFileToString(File settingsFile) { + String json; + try{ + FileReader file = new FileReader(settingsFile); + BufferedReader reader = new BufferedReader(file); + StringBuilder builder = new StringBuilder(); + int c; + while ((c = reader.read()) != -1) { + builder.append((char) c); + } + json = builder.toString(); + }catch (IOException e){ + json = null; + e.printStackTrace(); + } + return json; + } + public static void deleteDir(File dir) { if (dir.exists()) { for (File f : dir.listFiles()) { From aef6429dbcda73ae1f5f22f7ef4710f135d13ae9 Mon Sep 17 00:00:00 2001 From: OMGeeky Date: Tue, 24 Jun 2025 23:27:54 +0200 Subject: [PATCH 04/15] code style --- .idea/codeStyles/Project.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index 3e9de3b..0e36d52 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -10,5 +10,10 @@