diff --git a/src/com/gpl/rpg/atcontentstudio/model/Project.java b/src/com/gpl/rpg/atcontentstudio/model/Project.java index 9cea904..9d3c8b4 100644 --- a/src/com/gpl/rpg/atcontentstudio/model/Project.java +++ b/src/com/gpl/rpg/atcontentstudio/model/Project.java @@ -12,7 +12,6 @@ import java.io.StringWriter; import java.util.ArrayList; import java.util.Enumeration; import java.util.HashMap; -import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; @@ -45,7 +44,6 @@ 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.Project.ResourceSet; import com.gpl.rpg.atcontentstudio.model.gamedata.ActorCondition; import com.gpl.rpg.atcontentstudio.model.gamedata.Dialogue; import com.gpl.rpg.atcontentstudio.model.gamedata.Droplist; @@ -1060,74 +1058,13 @@ public class Project implements ProjectTreeNode, Serializable { } } - public void generateExportPackage(final File target) { + 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 = 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, Set> writtenFilesPerDataType = new LinkedHashMap, Set>(); - Set 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 LinkedHashSet(); - for (File createdMapFile : createdContent.gameMaps.mapFolder.listFiles()) { - FileUtils.copyFile(createdMapFile, new File(tmpMapDir, createdMapFile.getName())); - writtenFiles.add(createdMapFile.getName()); - } - for (File alteredMapFile : alteredContent.gameMaps.mapFolder.listFiles()) { - FileUtils.copyFile(alteredMapFile, new File(tmpMapDir, 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(); - } - } + File tmpDir = exportProjectToTmpDir(); FileUtils.writeToZip(tmpDir, target); FileUtils.deleteDir(tmpDir); @@ -1138,6 +1075,91 @@ public class Project implements ProjectTreeNode, Serializable { }); } + 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 = exportProjectToTmpDir(); + + FileUtils.copyOver(tmpDir, target); + FileUtils.deleteDir(tmpDir); + Notification.addSuccess("Project \""+name+"\" exported into "+target.getAbsolutePath()); + } + + + }); + } + + public File exportProjectToTmpDir() { + 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, Set> writtenFilesPerDataType = new LinkedHashMap, Set>(); + Set 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 LinkedHashSet(); + for (File createdMapFile : createdContent.gameMaps.mapFolder.listFiles()) { + FileUtils.copyFile(createdMapFile, new File(tmpMapDir, createdMapFile.getName())); + writtenFiles.add(createdMapFile.getName()); + } + for (File alteredMapFile : alteredContent.gameMaps.mapFolder.listFiles()) { + FileUtils.copyFile(alteredMapFile, new File(tmpMapDir, 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; + } + @SuppressWarnings("rawtypes") public Set writeDataDeltaForDataType(GameDataCategory created, GameDataCategory altered, GameDataCategory source, Class gdeClass, File targetFolder) { Set filenamesToWrite = new LinkedHashSet(); diff --git a/src/com/gpl/rpg/atcontentstudio/ui/Editor.java b/src/com/gpl/rpg/atcontentstudio/ui/Editor.java index 703e701..7c5e7ac 100644 --- a/src/com/gpl/rpg/atcontentstudio/ui/Editor.java +++ b/src/com/gpl/rpg/atcontentstudio/ui/Editor.java @@ -39,7 +39,6 @@ import javax.swing.JSpinner.NumberEditor; import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.ListModel; -import javax.swing.Scrollable; import javax.swing.SpinnerNumberModel; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; diff --git a/src/com/gpl/rpg/atcontentstudio/ui/ExportProjectWizard.java b/src/com/gpl/rpg/atcontentstudio/ui/ExportProjectWizard.java new file mode 100644 index 0000000..8179a29 --- /dev/null +++ b/src/com/gpl/rpg/atcontentstudio/ui/ExportProjectWizard.java @@ -0,0 +1,226 @@ +package com.gpl.rpg.atcontentstudio.ui; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.Toolkit; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.File; + +import javax.swing.ButtonGroup; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JDialog; +import javax.swing.JFileChooser; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JRadioButton; + +import com.gpl.rpg.atcontentstudio.ATContentStudio; +import com.gpl.rpg.atcontentstudio.model.Project; +import com.gpl.rpg.atcontentstudio.model.gamedata.GameDataSet; +import com.gpl.rpg.atcontentstudio.model.maps.TMXMapSet; +import com.gpl.rpg.atcontentstudio.model.sprites.SpriteSheetSet; +import com.jidesoft.swing.JideBoxLayout; + +public class ExportProjectWizard extends JDialog { + + private static final long serialVersionUID = -8745083621008868612L; + + JPanel pane; + JLabel errorLabel, fileSelectionLabel; + JRadioButton asZip, overSources; + JComboBox target; + JButton browse; + JButton okButton, cancelButton; + + Project proj; + + public ExportProjectWizard(Project proj) { + + super(ATContentStudio.frame); + setTitle("Export project for inclusion in-game"); + + this.proj = proj; + + pane = new JPanel(); + pane.setLayout(new JideBoxLayout(pane, JideBoxLayout.PAGE_AXIS, 6)); + + errorLabel = new JLabel(); + + pane.add(errorLabel, JideBoxLayout.FIX); + pane.add(new JLabel("Export this ATCS project..."), JideBoxLayout.FIX); + + ButtonGroup radioGroup = new ButtonGroup(); + + asZip = new JRadioButton("... as a Zip archive"); + radioGroup.add(asZip); + overSources = new JRadioButton("... into a game source folder"); + radioGroup.add(overSources); + asZip.setSelected(true); + + pane.add(asZip, JideBoxLayout.FIX); + pane.add(overSources, JideBoxLayout.FIX); + + ActionListener updateListener = new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + updateState(); + } + }; + asZip.addActionListener(updateListener); + overSources.addActionListener(updateListener); + + target = new JComboBox(); + target.setEditable(true); + target.addActionListener(updateListener); + browse = new JButton("Browse"); + browse.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + JFileChooser jfc = new JFileChooser(){ + private static final long serialVersionUID = -3001082967957619011L; + @Override + public boolean accept(File f) { + if (asZip.isSelected()) { + if (f.isDirectory() || f.getName().endsWith(".zip") || f.getName().endsWith(".ZIP")) { + return super.accept(f); + } else { + return false; + } + } else { + return f.isDirectory(); + } + } + }; + jfc.setFileSelectionMode(asZip.isSelected() ? JFileChooser.FILES_AND_DIRECTORIES : JFileChooser.DIRECTORIES_ONLY); + jfc.setSelectedFile(new File(target.getSelectedItem() == null ? "" : target.getSelectedItem().toString())); + jfc.setMultiSelectionEnabled(false); + int result = jfc.showOpenDialog(ATContentStudio.frame); + if (result == JFileChooser.APPROVE_OPTION) { + File f = jfc.getSelectedFile(); + if (asZip.isSelected() && !f.getAbsolutePath().substring(f.getAbsolutePath().length() - 4, f.getAbsolutePath().length()).equalsIgnoreCase(".zip")) { + f = new File(f.getAbsolutePath()+".zip"); + } + target.setSelectedItem(f.getAbsolutePath()); + updateState(); + } + } + }); + JPanel fileSelectionPane = new JPanel(); + fileSelectionPane.setLayout(new JideBoxLayout(fileSelectionPane, JideBoxLayout.LINE_AXIS, 6)); + fileSelectionLabel = new JLabel("Zip file: "); + fileSelectionPane.add(fileSelectionLabel, JideBoxLayout.FIX); + fileSelectionPane.add(target, JideBoxLayout.VARY); + fileSelectionPane.add(browse, JideBoxLayout.FIX); + + pane.add(fileSelectionPane, JideBoxLayout.FIX); + + JPanel buttonPane = new JPanel(); + buttonPane.setLayout(new JideBoxLayout(buttonPane, JideBoxLayout.LINE_AXIS, 6)); + buttonPane.add(new JPanel(), JideBoxLayout.VARY); + cancelButton = new JButton("Cancel"); + buttonPane.add(cancelButton, JideBoxLayout.FIX); + okButton = new JButton("Ok"); + buttonPane.add(okButton, JideBoxLayout.FIX); + + pane.add(new JPanel(), JideBoxLayout.VARY); + + pane.add(buttonPane, JideBoxLayout.FIX); + + + cancelButton.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent e) { + ExportProjectWizard.this.setVisible(false); + ExportProjectWizard.this.dispose(); + } + }); + + okButton.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent e) { + if (asZip.isSelected()) { + ExportProjectWizard.this.proj.exportProjectAsZipPackage(new File(target.getSelectedItem().toString())); + } else { + ExportProjectWizard.this.proj.exportProjectOverGameSource(new File(target.getSelectedItem().toString())); + } + ExportProjectWizard.this.setVisible(false); + ExportProjectWizard.this.dispose(); + } + }); + + updateState(); + + getContentPane().setLayout(new BorderLayout()); + getContentPane().add(pane, BorderLayout.CENTER); + + setMinimumSize(new Dimension(500,150)); + pack(); + + Dimension sdim = Toolkit.getDefaultToolkit().getScreenSize(); + Dimension wdim = getSize(); + setLocation((sdim.width - wdim.width)/2, (sdim.height - wdim.height)/2); + } + + private void updateState() { + if (asZip.isSelected()) { + fileSelectionLabel.setText("Zip file: "); + } else { + fileSelectionLabel.setText("Game source folder: "); + } + + + File f = new File(target.getSelectedItem() == null ? "" : target.getSelectedItem().toString()); + if (asZip.isSelected()) { + if (target.getSelectedItem() == null || target.getSelectedItem().toString().length() <= 0) { + errorLabel.setText("You must select where to save the zip file."); + okButton.setEnabled(false); + } else if (f.isDirectory()) { + errorLabel.setText("The selected target is a directory. It should be a zip file."); + okButton.setEnabled(false); + } else if (!(f.getName().toLowerCase().endsWith(".zip"))) { + errorLabel.setText("The selected target is not a zip file. It should be a zip file."); + okButton.setEnabled(false); + } else if (f.exists()) { + errorLabel.setText("The selected target is an existing zip file. It will be overwritten."); + okButton.setEnabled(true); + } else { + errorLabel.setText("Everything looks good !"); + okButton.setEnabled(true); + } + } else { + if (target.getSelectedItem() == null || target.getSelectedItem().toString().length() <= 0) { + errorLabel.setText("You must select an AT source root folder."); + okButton.setEnabled(false); + } else if (!f.isDirectory() || !f.exists()) { + errorLabel.setText("The selected AT source is not a folder. It should be an existing AT source root folder."); + okButton.setEnabled(false); + } else { + File res = new File(f, GameDataSet.DEFAULT_REL_PATH_IN_SOURCE); + File drawable = new File(f, SpriteSheetSet.DEFAULT_REL_PATH_IN_SOURCE); + File xml = new File(f, TMXMapSet.DEFAULT_REL_PATH_IN_SOURCE); + if (!res.exists()) { + errorLabel.setText("The selected AT source root folder does not contain the \"res\" folder."); + okButton.setEnabled(true); + } else if (!drawable.exists()) { + errorLabel.setText("The selected AT source root folder does not contain the \"drawable\" folder."); + okButton.setEnabled(true); + } else if (!xml.exists()) { + errorLabel.setText("The selected AT source root folder does not contain the \"xml\" folder."); + okButton.setEnabled(true); + } else { + errorLabel.setText("Everything looks good !"); + okButton.setEnabled(true); + } + } + } + revalidate(); + repaint(); + + } + + +} diff --git a/src/com/gpl/rpg/atcontentstudio/ui/WorkspaceActions.java b/src/com/gpl/rpg/atcontentstudio/ui/WorkspaceActions.java index 35123c8..af881d6 100644 --- a/src/com/gpl/rpg/atcontentstudio/ui/WorkspaceActions.java +++ b/src/com/gpl/rpg/atcontentstudio/ui/WorkspaceActions.java @@ -34,7 +34,6 @@ import com.gpl.rpg.atcontentstudio.model.gamedata.JSONElement; import com.gpl.rpg.atcontentstudio.model.gamedata.Quest; import com.gpl.rpg.atcontentstudio.model.gamedata.QuestStage; 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; @@ -353,21 +352,7 @@ public class WorkspaceActions { public ATCSAction exportProject = new ATCSAction("Export project", "Generates a zip file containing all the created & altered resources of the project, ready to merge with the game source."){ public void actionPerformed(ActionEvent e) { if (selectedNode == null || selectedNode.getProject() == null) return; - JFileChooser chooser = new JFileChooser() { - private static final long serialVersionUID = 8039332384370636746L; - public boolean accept(File f) { - return f.isDirectory() || f.getName().endsWith(".zip") || f.getName().endsWith(".ZIP"); - } - }; - chooser.setMultiSelectionEnabled(false); - int result = chooser.showSaveDialog(ATContentStudio.frame); - if (result == JFileChooser.APPROVE_OPTION) { - File f = chooser.getSelectedFile(); - if (!f.getAbsolutePath().substring(f.getAbsolutePath().length() - 4, f.getAbsolutePath().length()).equalsIgnoreCase(".zip")) { - f = new File(f.getAbsolutePath()+".zip"); - } - selectedNode.getProject().generateExportPackage(f); - } + new ExportProjectWizard(selectedNode.getProject()).setVisible(true); }; public void selectionChanged(ProjectTreeNode selectedNode, TreePath[] selectedPaths) { setEnabled(selectedNode != null && selectedNode.getProject() != null); diff --git a/src/com/gpl/rpg/atcontentstudio/ui/tools/writermode/WriterModeEditor.java b/src/com/gpl/rpg/atcontentstudio/ui/tools/writermode/WriterModeEditor.java index a83c68e..7445dbe 100644 --- a/src/com/gpl/rpg/atcontentstudio/ui/tools/writermode/WriterModeEditor.java +++ b/src/com/gpl/rpg/atcontentstudio/ui/tools/writermode/WriterModeEditor.java @@ -10,13 +10,11 @@ import java.awt.event.FocusListener; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; import java.awt.geom.Point2D; -import java.io.IOException; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import javax.imageio.ImageIO; import javax.swing.AbstractAction; import javax.swing.ImageIcon; import javax.swing.JButton; diff --git a/src/com/gpl/rpg/atcontentstudio/utils/FileUtils.java b/src/com/gpl/rpg/atcontentstudio/utils/FileUtils.java index c25464f..4e54ae8 100644 --- a/src/com/gpl/rpg/atcontentstudio/utils/FileUtils.java +++ b/src/com/gpl/rpg/atcontentstudio/utils/FileUtils.java @@ -78,6 +78,29 @@ public class FileUtils { } + /** + * 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;