diff --git a/.gitignore b/.gitignore index 2bdf504..1737c04 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,6 @@ META-INF/MANIFEST.MF +.idea/ +libs/ +out/ +*.iml \ No newline at end of file diff --git a/fr/rader/billy/Logger.java b/fr/rader/billy/Logger.java new file mode 100644 index 0000000..e794670 --- /dev/null +++ b/fr/rader/billy/Logger.java @@ -0,0 +1,125 @@ +package fr.rader.billy; + +import javax.swing.*; +import java.io.*; +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +public class Logger { + + private final File LOG_OUTPUT = new File(System.getenv("APPDATA") + "/.minecraft/logs/billy.log"); + + private List unusedFields = new ArrayList<>(); + + public void writeln(String message) { + write(message + "\n"); + } + + public void write(String message) { + if(logExists()) { + try { + PrintWriter writer = new PrintWriter(new BufferedWriter(new FileWriter(LOG_OUTPUT, true))); + + writer.print("[" + currentTime() + "]: " + message); + + writer.flush(); + writer.close(); + } catch (IOException ioException) { + ioException.printStackTrace(); + } + } + } + + public boolean logExists() { + return LOG_OUTPUT.exists(); + } + + public boolean createLog() { + try { + return LOG_OUTPUT.createNewFile(); + } catch (IOException ioException) { + ioException.printStackTrace(); + } + + return false; + } + + public void clearLog() { + LOG_OUTPUT.delete(); + + try { + LOG_OUTPUT.createNewFile(); + } catch (IOException ioException) { + ioException.printStackTrace(); + } + } + + public String currentDate() { + LocalDate date = LocalDate.now(); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy"); + return date.format(formatter); + } + + public String currentTime() { + LocalTime date = LocalTime.now(); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm:ss"); + return date.format(formatter); + } + + public String readProperties(Map properties) { + String out = ""; + + for(Map.Entry property : properties.entrySet()) { + out += property.getKey() + "="; + if(property.getValue() instanceof double[]) { + out += Arrays.toString((double[]) property.getValue()) + "; "; + } else { + out += property.getValue() + "; "; + } + } + + return out; + } + + public void exception(Exception exception) { + StringWriter sw = new StringWriter(); + exception.printStackTrace(new PrintWriter(sw)); + writeln("Caught exception:\n" + sw.toString().substring(0, sw.toString().length() - 2)); + } + + public void exception(Exception exception, String replay) { + String message = exception.getLocalizedMessage(); + int index = Integer.parseInt(message.split("column ")[1].split(" ")[0]); + writeln("Crash occured here (index:" + index + "): " + replay.substring(Math.max(index - 30, 0), index)); + + printUnused(replay); + exception(exception); + + writeln("Timelines dump: " + replay); + + JOptionPane.showMessageDialog(null, "ERROR: " + exception.getLocalizedMessage() + + "\nPlease open an issue on https://github.com/Nemos59/Billy/issues, and provide the \".minecraft\\logs\\billy.log\" file."); + } + + public void printUnused(String replay) { + for(String field : unusedFields) { + int min = replay.split(field)[0].length() - 50; + if(min < 0) min = 0; + + writeln("Unused field: " + field + " => " + replay.split(field)[0].substring(min) + field + "\" <- HERE "); + } + } + + public void addUnusedField(String field) { + unusedFields.add(field); + } + + public void clearUnusedFields() { + unusedFields.clear(); + } +} diff --git a/fr/rader/billy/Main.java b/fr/rader/billy/Main.java index 2e2de27..65e8aab 100644 --- a/fr/rader/billy/Main.java +++ b/fr/rader/billy/Main.java @@ -11,9 +11,21 @@ public class Main { private static Main instance; + private Logger logger = new Logger(); + public JCheckBoxMenuItem saveToDefaultFolder; private void start() { + if(!logger.logExists()) { + logger.createLog(); + } else { + logger.clearLog(); + } + + logger.writeln("Starting Billy"); + logger.writeln("Current date is: " + logger.currentDate()); + + logger.writeln("Checking folders..."); // Check if every folders exists File toCheck = new File(OpenReplayListener.REPLAY_RECORDINGS); if(!toCheck.exists()) JOptionPane.showMessageDialog(null, "The replay_recordings folder does not exist."); @@ -24,6 +36,7 @@ private void start() { toCheck = new File(OpenReplayListener.RIGHT_SIDE); if(!toCheck.exists()) toCheck.mkdirs(); + logger.writeln("Creating interface"); createInterface(); } @@ -96,4 +109,8 @@ private Main() { public static Main getInstance() { return instance; } + + public Logger getLogger() { + return this.logger; + } } diff --git a/fr/rader/billy/gui/inspector/listeners/SaveKeyframeListener.java b/fr/rader/billy/gui/inspector/listeners/SaveKeyframeListener.java index 436b512..fc84193 100644 --- a/fr/rader/billy/gui/inspector/listeners/SaveKeyframeListener.java +++ b/fr/rader/billy/gui/inspector/listeners/SaveKeyframeListener.java @@ -1,5 +1,7 @@ package fr.rader.billy.gui.inspector.listeners; +import fr.rader.billy.Logger; +import fr.rader.billy.Main; import fr.rader.billy.gui.inspector.TimelineInspector; import fr.rader.billy.timeline.Keyframe; @@ -10,6 +12,8 @@ public class SaveKeyframeListener implements ActionListener { + private Logger logger = Main.getInstance().getLogger(); + @Override public void actionPerformed(ActionEvent e) { TimelineInspector timelineInspector = TimelineInspector.getInstance(); @@ -33,11 +37,17 @@ public void actionPerformed(ActionEvent e) { boolean isSpectator = timelineInspector.spectatorKeyframeCheck.isSelected(); + logger.writeln("Started saving position keyframe #" + selectedKeyframe); + logger.writeln("Writing timestamp: " + timestamp); + logger.writeln("Writing spectator: " + isSpectator); + Map properties = new HashMap<>(); properties.put("camera:rotation", rotation); properties.put("camera:position", position); if(isSpectator) properties.put("spectate", 1); + logger.writeln("Writing properties: " + logger.readProperties(properties)); + Keyframe newKeyframe = new Keyframe(timestamp, properties); timelineInspector.positionPath.getKeyframes().set(selectedKeyframe, newKeyframe); } else { @@ -45,9 +55,15 @@ public void actionPerformed(ActionEvent e) { int replayTimestamp = Integer.parseInt(timelineInspector.replayTimestampField.getText()); + logger.writeln("Started saving time keyframe #" + selectedKeyframe); + logger.writeln("Writing keyframe timestamp: " + timestamp); + logger.writeln("Writing replay timestamp: " + replayTimestamp); + Map properties = new HashMap<>(); properties.put("timestamp", replayTimestamp); + logger.writeln("Writing properties: " + logger.readProperties(properties)); + Keyframe newKeyframe = new Keyframe(timestamp, properties); timelineInspector.timePath.getKeyframes().set(selectedKeyframe, newKeyframe); } diff --git a/fr/rader/billy/gui/inspector/listeners/ShiftReplayTimestampListener.java b/fr/rader/billy/gui/inspector/listeners/ShiftReplayTimestampListener.java index fa062bd..593390b 100644 --- a/fr/rader/billy/gui/inspector/listeners/ShiftReplayTimestampListener.java +++ b/fr/rader/billy/gui/inspector/listeners/ShiftReplayTimestampListener.java @@ -1,5 +1,7 @@ package fr.rader.billy.gui.inspector.listeners; +import fr.rader.billy.Logger; +import fr.rader.billy.Main; import fr.rader.billy.gui.inspector.TimelineInspector; import fr.rader.billy.timeline.Keyframe; @@ -8,12 +10,16 @@ public class ShiftReplayTimestampListener implements ActionListener { + private Logger logger = Main.getInstance().getLogger(); + @Override public void actionPerformed(ActionEvent e) { TimelineInspector timelineInspector = TimelineInspector.getInstance(); int shift = (int) timelineInspector.shiftTimelineSpinner.getValue(); + logger.writeln("Shifting replay timestamps: " + shift); + for(Keyframe keyframe : timelineInspector.timePath.getKeyframes()) { keyframe.getProperties().replace("timestamp", (Integer) keyframe.getProperties().get("timestamp") + shift); } diff --git a/fr/rader/billy/gui/inspector/listeners/ShiftTimelineListener.java b/fr/rader/billy/gui/inspector/listeners/ShiftTimelineListener.java index dfc6421..d43854a 100644 --- a/fr/rader/billy/gui/inspector/listeners/ShiftTimelineListener.java +++ b/fr/rader/billy/gui/inspector/listeners/ShiftTimelineListener.java @@ -1,5 +1,7 @@ package fr.rader.billy.gui.inspector.listeners; +import fr.rader.billy.Logger; +import fr.rader.billy.Main; import fr.rader.billy.gui.inspector.TimelineInspector; import fr.rader.billy.timeline.Keyframe; @@ -8,12 +10,16 @@ public class ShiftTimelineListener implements ActionListener { + private Logger logger = Main.getInstance().getLogger(); + @Override public void actionPerformed(ActionEvent e) { TimelineInspector timelineInspector = TimelineInspector.getInstance(); int shift = (int) timelineInspector.shiftTimelineSpinner.getValue(); + logger.writeln("Shifting timeline: " + shift); + for(Keyframe keyframe : timelineInspector.timePath.getKeyframes()) { keyframe.setTime(keyframe.getTime() + shift); } diff --git a/fr/rader/billy/gui/inspector/listeners/TranslateTimelineListener.java b/fr/rader/billy/gui/inspector/listeners/TranslateTimelineListener.java index c8ce686..db664bf 100644 --- a/fr/rader/billy/gui/inspector/listeners/TranslateTimelineListener.java +++ b/fr/rader/billy/gui/inspector/listeners/TranslateTimelineListener.java @@ -1,13 +1,18 @@ package fr.rader.billy.gui.inspector.listeners; +import fr.rader.billy.Logger; +import fr.rader.billy.Main; import fr.rader.billy.gui.inspector.TimelineInspector; import fr.rader.billy.timeline.Keyframe; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.util.Arrays; public class TranslateTimelineListener implements ActionListener { + private Logger logger = Main.getInstance().getLogger(); + @Override public void actionPerformed(ActionEvent e) { TimelineInspector timelineInspector = TimelineInspector.getInstance(); @@ -22,6 +27,8 @@ public void actionPerformed(ActionEvent e) { position[0] += diffX; position[1] += diffY; position[2] += diffZ; + + logger.writeln("Translating timeline: " + Arrays.toString(position)); } timelineInspector.xField.setText(String.valueOf(Double.parseDouble(timelineInspector.xField.getText()) + diffX)); diff --git a/fr/rader/billy/gui/main/listeners/CopyTimelineListener.java b/fr/rader/billy/gui/main/listeners/CopyTimelineListener.java index 7105c3a..d950d0e 100644 --- a/fr/rader/billy/gui/main/listeners/CopyTimelineListener.java +++ b/fr/rader/billy/gui/main/listeners/CopyTimelineListener.java @@ -1,16 +1,21 @@ package fr.rader.billy.gui.main.listeners; +import fr.rader.billy.Logger; +import fr.rader.billy.Main; import fr.rader.billy.gui.main.MainInterface; import fr.rader.billy.timeline.Timeline; import javax.swing.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.util.Arrays; import java.util.List; import java.util.Map; public class CopyTimelineListener implements ActionListener { + private Logger logger = Main.getInstance().getLogger(); + private MainInterface mainInterface; @Override @@ -35,6 +40,8 @@ public void actionPerformed(ActionEvent e) { private void copyToList(List selectedTimelines, Map fromList, Map toList, String replayName) { if(replayName.equals("Open Replay")) return; + logger.writeln("Copying timelines " + Arrays.toString(selectedTimelines.toArray()) + " to '" + replayName + "'"); + for(String name : selectedTimelines) { String oldName = name; while(toList.containsKey(name)) { diff --git a/fr/rader/billy/gui/main/listeners/DeleteTimelineListener.java b/fr/rader/billy/gui/main/listeners/DeleteTimelineListener.java index 507a559..7112128 100644 --- a/fr/rader/billy/gui/main/listeners/DeleteTimelineListener.java +++ b/fr/rader/billy/gui/main/listeners/DeleteTimelineListener.java @@ -1,15 +1,20 @@ package fr.rader.billy.gui.main.listeners; +import fr.rader.billy.Logger; +import fr.rader.billy.Main; import fr.rader.billy.gui.main.MainInterface; import fr.rader.billy.timeline.Timeline; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.util.Arrays; import java.util.List; import java.util.Map; public class DeleteTimelineListener implements ActionListener { + private Logger logger = Main.getInstance().getLogger(); + private MainInterface mainInterface; @Override @@ -28,6 +33,8 @@ public void actionPerformed(ActionEvent e) { } private void deleteSelectedTimelines(List selectedTimelines, Map timelinesList) { + logger.writeln("Deleting timelines " + Arrays.toString(selectedTimelines.toArray())); + for(String name : selectedTimelines) { timelinesList.remove(name); } diff --git a/fr/rader/billy/gui/main/listeners/MoveTimelineListener.java b/fr/rader/billy/gui/main/listeners/MoveTimelineListener.java index 7b1a547..5e0d818 100644 --- a/fr/rader/billy/gui/main/listeners/MoveTimelineListener.java +++ b/fr/rader/billy/gui/main/listeners/MoveTimelineListener.java @@ -1,16 +1,21 @@ package fr.rader.billy.gui.main.listeners; +import fr.rader.billy.Logger; +import fr.rader.billy.Main; import fr.rader.billy.gui.main.MainInterface; import fr.rader.billy.timeline.Timeline; import javax.swing.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.util.Arrays; import java.util.List; import java.util.Map; public class MoveTimelineListener implements ActionListener { + private Logger logger = Main.getInstance().getLogger(); + private MainInterface mainInterface; @Override @@ -35,6 +40,8 @@ public void actionPerformed(ActionEvent e) { private void moveToList(List selectedTimelines, Map fromList, Map toList, String replayName) { if(replayName.equals("Open Replay")) return; + logger.writeln("Moving timelines " + Arrays.toString(selectedTimelines.toArray()) + " to '" + replayName + "'"); + for(String name : selectedTimelines) { String oldName = name; while(toList.containsKey(name)) { diff --git a/fr/rader/billy/gui/main/listeners/OpenReplayListener.java b/fr/rader/billy/gui/main/listeners/OpenReplayListener.java index 6b13a59..397f6e6 100644 --- a/fr/rader/billy/gui/main/listeners/OpenReplayListener.java +++ b/fr/rader/billy/gui/main/listeners/OpenReplayListener.java @@ -1,5 +1,7 @@ package fr.rader.billy.gui.main.listeners; +import fr.rader.billy.Logger; +import fr.rader.billy.Main; import fr.rader.billy.gui.main.MainInterface; import fr.rader.billy.timeline.TimelineSerialization; import net.lingala.zip4j.ZipFile; @@ -17,9 +19,11 @@ public class OpenReplayListener implements ActionListener { - public static final String REPLAY_RECORDINGS = System.getenv("APPDATA") + "/.minecraft/replay_recordings/"; - public static final String LEFT_SIDE = REPLAY_RECORDINGS + "extracted_timelines/left/"; - public static final String RIGHT_SIDE = REPLAY_RECORDINGS + "extracted_timelines/right/"; + private static Logger logger = Main.getInstance().getLogger(); + + public static final String REPLAY_RECORDINGS = System.getenv("APPDATA") + "\\.minecraft\\replay_recordings\\"; + public static final String LEFT_SIDE = REPLAY_RECORDINGS + "extracted_timelines\\left\\"; + public static final String RIGHT_SIDE = REPLAY_RECORDINGS + "extracted_timelines\\right\\"; private static File[] files = new File[] { new File(REPLAY_RECORDINGS), @@ -55,23 +59,13 @@ private static void startLoadingReplay(String side, boolean isOpenLeft) { } if(isOpenLeft) { - try { - if(hasTimeline) mainInterface.leftTimelineList = serialization.deserialize(new File(side + "timelines.json")); - else if(mainInterface.leftTimelineList != null && !mainInterface.leftTimelineList.isEmpty()) mainInterface.leftTimelineList.clear(); - else if(mainInterface.leftTimelineList == null) mainInterface.leftTimelineList = new HashMap<>(); - } catch (IOException ioException) { - JOptionPane.showMessageDialog(null, "Error while deserializing: " + ioException.getLocalizedMessage()); - return; - } + if(hasTimeline) mainInterface.leftTimelineList = serialization.deserialize(new File(side + "timelines.json")); + else if(mainInterface.leftTimelineList != null && !mainInterface.leftTimelineList.isEmpty()) mainInterface.leftTimelineList.clear(); + else if(mainInterface.leftTimelineList == null) mainInterface.leftTimelineList = new HashMap<>(); } else { - try { - if(hasTimeline) mainInterface.rightTimelineList = serialization.deserialize(new File(side + "timelines.json")); - else if(mainInterface.rightTimelineList != null && !mainInterface.rightTimelineList.isEmpty()) mainInterface.rightTimelineList.clear(); - else if(mainInterface.rightTimelineList == null) mainInterface.rightTimelineList = new HashMap<>(); - } catch (IOException ioException) { - JOptionPane.showMessageDialog(null, "Error while deserializing: " + ioException.getLocalizedMessage()); - return; - } + if(hasTimeline) mainInterface.rightTimelineList = serialization.deserialize(new File(side + "timelines.json")); + else if(mainInterface.rightTimelineList != null && !mainInterface.rightTimelineList.isEmpty()) mainInterface.rightTimelineList.clear(); + else if(mainInterface.rightTimelineList == null) mainInterface.rightTimelineList = new HashMap<>(); } mainInterface.updateNames(); @@ -95,7 +89,10 @@ private static void openReplay(File file, String side) { else hasNewReplay = false; } + logger.writeln("Opening Replay '" + file.getName() + "' on side " + side); + if(!hasNewReplay) { + logger.writeln("Same replay!"); JOptionPane.showMessageDialog(null, "Replays must be different!"); return; } @@ -119,6 +116,8 @@ private static void openReplay(File file, String side) { JOptionPane.showMessageDialog(null, "Warning:\n" + zipException.getLocalizedMessage() + "\nAssuming the Replay does not contain a timeline."); } } + + logger.writeln("Done!"); } private File openFilePrompt() { @@ -157,6 +156,7 @@ public static File getLeftFile() { public static void refreshLeft() { if(!files[1].equals(new File(""))) { + logger.writeln("Refreshing left replay..."); hasNewReplay = true; openReplay(files[1], LEFT_SIDE); startLoadingReplay(LEFT_SIDE, true); @@ -165,6 +165,7 @@ public static void refreshLeft() { public static void refreshRight() { if(!files[2].equals(new File(""))) { + logger.writeln("Refreshing right replay..."); hasNewReplay = true; openReplay(files[2], RIGHT_SIDE); startLoadingReplay(RIGHT_SIDE, false); diff --git a/fr/rader/billy/gui/main/listeners/ReloadReplayListener.java b/fr/rader/billy/gui/main/listeners/ReloadReplayListener.java index 3d84079..4b3006e 100644 --- a/fr/rader/billy/gui/main/listeners/ReloadReplayListener.java +++ b/fr/rader/billy/gui/main/listeners/ReloadReplayListener.java @@ -9,7 +9,6 @@ public class ReloadReplayListener implements ActionListener { @Override public void actionPerformed(ActionEvent e) { - if(e.getSource().equals(MainInterface.getInstance().reloadLeftReplayButton)) { OpenReplayListener.refreshLeft(); } else { diff --git a/fr/rader/billy/gui/main/listeners/RenameTimelineListener.java b/fr/rader/billy/gui/main/listeners/RenameTimelineListener.java index b41a0ec..7b90526 100644 --- a/fr/rader/billy/gui/main/listeners/RenameTimelineListener.java +++ b/fr/rader/billy/gui/main/listeners/RenameTimelineListener.java @@ -1,16 +1,21 @@ package fr.rader.billy.gui.main.listeners; +import fr.rader.billy.Logger; +import fr.rader.billy.Main; import fr.rader.billy.gui.main.MainInterface; import fr.rader.billy.timeline.Timeline; import javax.swing.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.util.Arrays; import java.util.List; import java.util.Map; public class RenameTimelineListener implements ActionListener { + private Logger logger = Main.getInstance().getLogger(); + private MainInterface mainInterface; @Override @@ -29,6 +34,8 @@ public void actionPerformed(ActionEvent e) { } private void renameTimelines(List selectedTimelines, Map timelineList) { + logger.writeln("Renaming timelines " + Arrays.toString(selectedTimelines.toArray())); + for(String selectedTimeline : selectedTimelines) { String newTimelineName = JOptionPane.showInputDialog(null, "Enter a new name for \"" + selectedTimeline + "\" or press cancel to cancel", selectedTimeline); diff --git a/fr/rader/billy/gui/main/listeners/SaveReplayListener.java b/fr/rader/billy/gui/main/listeners/SaveReplayListener.java index f6522b7..7542986 100644 --- a/fr/rader/billy/gui/main/listeners/SaveReplayListener.java +++ b/fr/rader/billy/gui/main/listeners/SaveReplayListener.java @@ -1,5 +1,6 @@ package fr.rader.billy.gui.main.listeners; +import fr.rader.billy.Logger; import fr.rader.billy.Main; import fr.rader.billy.gui.main.MainInterface; import fr.rader.billy.timeline.TimelineSerialization; @@ -16,76 +17,75 @@ public class SaveReplayListener implements ActionListener { + private Logger logger = Main.getInstance().getLogger(); + private File lastFolderOpened; @Override public void actionPerformed(ActionEvent e) { TimelineSerialization serialization = new TimelineSerialization(); MainInterface mainInterface = MainInterface.getInstance(); - - try { - if(e.getSource() instanceof JButton) { - boolean right = e.getSource().equals(mainInterface.saveRightReplayButton); - - try { - if(right && !mainInterface.saveRightReplayButton.getText().equals("Open Replay")) { - saveTimeline(serialization.serialize(mainInterface.rightTimelineList), - OpenReplayListener.RIGHT_SIDE + "timelines.json", - OpenReplayListener.getRightFile(), - null); - } else if(!right && !mainInterface.saveLeftReplayButton.getText().equals("Open Replay")) { - saveTimeline(serialization.serialize(mainInterface.leftTimelineList), - OpenReplayListener.LEFT_SIDE + "timelines.json", - OpenReplayListener.getLeftFile(), - null); - } - } catch (IOException ioException) { - ioException.printStackTrace(); - } - } else { - switch (((JMenuItem) e.getSource()).getText()) { - case "Save Left As...": - saveTimeline(serialization.serialize(mainInterface.leftTimelineList), - OpenReplayListener.LEFT_SIDE + "timelines.json", - OpenReplayListener.getLeftFile(), - openFilePrompt()); - break; - case "Save Right As...": - saveTimeline(serialization.serialize(mainInterface.rightTimelineList), - OpenReplayListener.RIGHT_SIDE + "timelines.json", - OpenReplayListener.getRightFile(), - openFilePrompt()); - break; - case "Save Both": - saveTimeline(serialization.serialize(mainInterface.rightTimelineList), - OpenReplayListener.RIGHT_SIDE + "timelines.json", - OpenReplayListener.getRightFile(), - null); - saveTimeline(serialization.serialize(mainInterface.leftTimelineList), - OpenReplayListener.LEFT_SIDE + "timelines.json", - OpenReplayListener.getLeftFile(), - null); - break; - case "Save Right": - saveTimeline(serialization.serialize(mainInterface.rightTimelineList), - OpenReplayListener.RIGHT_SIDE + "timelines.json", - OpenReplayListener.getRightFile(), - null); - break; - case "Save Left": - saveTimeline(serialization.serialize(mainInterface.leftTimelineList), - OpenReplayListener.LEFT_SIDE + "timelines.json", - OpenReplayListener.getLeftFile(), - null); - break; + logger.writeln("Started saving timelines"); + + if(e.getSource() instanceof JButton) { + boolean right = e.getSource().equals(mainInterface.saveRightReplayButton); + + if(right && !mainInterface.saveRightReplayButton.getText().equals("Open Replay")) { + saveTimeline(serialization.serialize(mainInterface.rightTimelineList), + OpenReplayListener.RIGHT_SIDE + "timelines.json", + OpenReplayListener.getRightFile(), + null); + } else if(!right && !mainInterface.saveLeftReplayButton.getText().equals("Open Replay")) { + saveTimeline(serialization.serialize(mainInterface.leftTimelineList), + OpenReplayListener.LEFT_SIDE + "timelines.json", + OpenReplayListener.getLeftFile(), + null); } + } else { + switch (((JMenuItem) e.getSource()).getText()) { + case "Save Left As...": + saveTimeline(serialization.serialize(mainInterface.leftTimelineList), + OpenReplayListener.LEFT_SIDE + "timelines.json", + OpenReplayListener.getLeftFile(), + openFilePrompt()); + break; + case "Save Right As...": + saveTimeline(serialization.serialize(mainInterface.rightTimelineList), + OpenReplayListener.RIGHT_SIDE + "timelines.json", + OpenReplayListener.getRightFile(), + openFilePrompt()); + break; + case "Save Both": + saveTimeline(serialization.serialize(mainInterface.rightTimelineList), + OpenReplayListener.RIGHT_SIDE + "timelines.json", + OpenReplayListener.getRightFile(), + null); + saveTimeline(serialization.serialize(mainInterface.leftTimelineList), + OpenReplayListener.LEFT_SIDE + "timelines.json", + OpenReplayListener.getLeftFile(), + null); + break; + case "Save Right": + saveTimeline(serialization.serialize(mainInterface.rightTimelineList), + OpenReplayListener.RIGHT_SIDE + "timelines.json", + OpenReplayListener.getRightFile(), + null); + break; + case "Save Left": + saveTimeline(serialization.serialize(mainInterface.leftTimelineList), + OpenReplayListener.LEFT_SIDE + "timelines.json", + OpenReplayListener.getLeftFile(), + null); + break; } - } catch (IOException ioException) { - ioException.printStackTrace(); } + + logger.writeln("Done!"); } private void saveTimeline(String serializedTimeline, String timelinesFile, File mcprFile, File outputFile) { + logger.writeln("Saving timelines to '" + mcprFile.getName() + "'"); + try { // Writing the serialized timeline in timelines file FileWriter writer = new FileWriter(timelinesFile); @@ -124,6 +124,8 @@ private void saveTimeline(String serializedTimeline, String timelinesFile, File JOptionPane.showMessageDialog(null, "Saved to " + timelinesFile.replace("/", "\\")); } catch (IOException ioException) { + logger.exception(ioException); + JOptionPane.showMessageDialog(null, ioException.getLocalizedMessage()); } } diff --git a/fr/rader/billy/timeline/TimelineSerialization.java b/fr/rader/billy/timeline/TimelineSerialization.java index 418e808..ca176af 100644 --- a/fr/rader/billy/timeline/TimelineSerialization.java +++ b/fr/rader/billy/timeline/TimelineSerialization.java @@ -2,98 +2,111 @@ import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonWriter; +import fr.rader.billy.Logger; +import fr.rader.billy.Main; import java.io.*; import java.util.*; public class TimelineSerialization { - public String serialize(Map timelines) throws IOException { + private Logger logger = Main.getInstance().getLogger(); + + public String serialize(Map timelines) { StringWriter stringWriter = new StringWriter(); JsonWriter writer = new JsonWriter(stringWriter); + logger.writeln("Started serializing timelines"); + // writing timeline - writer.beginObject(); - for(Map.Entry entry : timelines.entrySet()) { - Timeline timeline = entry.getValue(); + try { + writer.beginObject(); + for(Map.Entry entry : timelines.entrySet()) { + logger.writeln("Serializing timeline '" + entry.getKey() + "'"); - String timelineName = entry.getKey(); - if(timelineName.equals("\uD835\uDE25\uD835\uDE26\uD835\uDE27\uD835\uDE22\uD835\uDE36\uD835\uDE2D\uD835\uDE35")) { - timelineName = ""; - } + Timeline timeline = entry.getValue(); - writer.name(timelineName).beginArray(); + String timelineName = entry.getKey(); + if(timelineName.equals("\uD835\uDE25\uD835\uDE26\uD835\uDE27\uD835\uDE22\uD835\uDE36\uD835\uDE2D\uD835\uDE35")) { + timelineName = ""; + } - // writing paths - for(Path path : timeline.getPaths()) { - writer.beginObject(); - writer.name("keyframes").beginArray(); + writer.name(timelineName).beginArray(); - // writing keyframes - for(Keyframe keyframe : path.getKeyframes()) { + // writing paths + for(Path path : timeline.getPaths()) { writer.beginObject(); - writer.name("time").value(keyframe.getTime()); - writer.name("properties").beginObject(); - - for(Map.Entry property : keyframe.getProperties().entrySet()) { - if(property.getKey().equals("timestamp")) { - writer.name("timestamp").value((Integer) property.getValue()); - } + writer.name("keyframes").beginArray(); - if(property.getKey().equals("spectate")) { - writer.name("spectate").value((Integer) property.getValue()); - } + // writing keyframes + for(Keyframe keyframe : path.getKeyframes()) { + writer.beginObject(); + writer.name("time").value(keyframe.getTime()); + writer.name("properties").beginObject(); - if(property.getKey().equals("interpolationFixed")) { - writer.name("interpolationFixed").nullValue(); - } + for(Map.Entry property : keyframe.getProperties().entrySet()) { + if(property.getKey().equals("timestamp")) { + writer.name("timestamp").value((Integer) property.getValue()); + } - if(property.getKey().equals("camera:rotation") || property.getKey().equals("camera:position")) { - writer.name(property.getKey()).beginArray(); + if(property.getKey().equals("spectate")) { + writer.name("spectate").value((Integer) property.getValue()); + } - for(double value : (double[]) property.getValue()) { - writer.value(value); + if(property.getKey().equals("interpolationFixed")) { + writer.name("interpolationFixed").nullValue(); } - writer.endArray(); - } - } + if(property.getKey().equals("camera:rotation") || property.getKey().equals("camera:position")) { + writer.name(property.getKey()).beginArray(); - writer.endObject(); - writer.endObject(); - } + for(double value : (double[]) property.getValue()) { + writer.value(value); + } - writer.endArray(); + writer.endArray(); + } + } - // writing segments - writer.name("segments").beginArray(); - for(int segment : path.getSegments()) { - writer.value(segment); - } + writer.endObject(); + writer.endObject(); + } - writer.endArray(); + writer.endArray(); - // writing interpolators - writer.name("interpolators").beginArray(); - for(Interpolator interpolator : path.getInterpolators()) { - writer.beginObject(); - writer.name("type"); + // writing segments + writer.name("segments").beginArray(); + for(int segment : path.getSegments()) { + writer.value(segment); + } - if(interpolator.getAlpha() == null) { - writer.value(interpolator.getType()); - } else { + writer.endArray(); + + // writing interpolators + writer.name("interpolators").beginArray(); + for(Interpolator interpolator : path.getInterpolators()) { writer.beginObject(); - writer.name("type").value(interpolator.getType()); - writer.name("alpha").value(Double.parseDouble(interpolator.getAlpha())); + writer.name("type"); - writer.endObject(); - } + if(interpolator.getAlpha() == null) { + writer.value(interpolator.getType()); + } else { + writer.beginObject(); + writer.name("type").value(interpolator.getType()); + writer.name("alpha").value(Double.parseDouble(interpolator.getAlpha())); - writer.name("properties").beginArray(); - for(String property : interpolator.getProperties()) { - if(property != null) { - writer.value(property); + writer.endObject(); } + + writer.name("properties").beginArray(); + for(String property : interpolator.getProperties()) { + if(property != null) { + writer.value(property); + } + } + + writer.endArray(); + writer.endObject(); } writer.endArray(); @@ -101,19 +114,20 @@ public String serialize(Map timelines) throws IOException { } writer.endArray(); - writer.endObject(); } - writer.endArray(); - } + writer.endObject(); + writer.flush(); - writer.endObject(); - writer.flush(); + logger.writeln("Done!"); + } catch (Exception exception) { + logger.exception(exception); + } return stringWriter.toString(); } - public Map deserialize(File timelineFile) throws IOException { + public Map deserialize(File timelineFile) { String replay = timelineToString(timelineFile); if(replay == null) return null; @@ -121,169 +135,207 @@ public Map deserialize(File timelineFile) throws IOException { JsonReader reader = new JsonReader(new StringReader(replay)); Map timelines = new LinkedHashMap<>(); - reader.beginObject(); - while(reader.hasNext()) { - String timelineName = reader.nextName(); + logger.writeln("Started deserializing timelines"); + logger.clearUnusedFields(); - if(timelineName.equals("")) { - timelineName = "\uD835\uDE25\uD835\uDE26\uD835\uDE27\uD835\uDE22\uD835\uDE36\uD835\uDE2D\uD835\uDE35"; - } - - List paths = new ArrayList<>(); - - // Read timelines - reader.beginArray(); + try { + reader.beginObject(); while(reader.hasNext()) { - List keyframes = new ArrayList<>(); - List segments = new ArrayList<>(); - List interpolators = new ArrayList<>(); + String timelineName = reader.nextName(); - // Read Paths - reader.beginObject(); - while(reader.hasNext()) { - switch(reader.nextName()) { - case "keyframes": - long time = 0; - Map keyframeProperties; + if(timelineName.equals("")) { + timelineName = "\uD835\uDE25\uD835\uDE26\uD835\uDE27\uD835\uDE22\uD835\uDE36\uD835\uDE2D\uD835\uDE35"; + } - // Read Keyframe - reader.beginArray(); - while(reader.hasNext()) { - keyframeProperties = new HashMap<>(); + List paths = new ArrayList<>(); - reader.beginObject(); - while(reader.hasNext()) { - switch(reader.nextName()) { - case "time": - time = reader.nextLong(); - break; - - case "properties": - reader.beginObject(); - while(reader.hasNext()) { - String nextName = reader.nextName(); - - switch(nextName) { - case "timestamp": - keyframeProperties.put("timestamp", reader.nextInt()); - break; - - case "spectate": - keyframeProperties.put("spectate", reader.nextInt()); - break; - - case "interpolationFixed": - keyframeProperties.put("interpolationFixed", null); - reader.nextNull(); - break; - - case "camera:rotation": - case "camera:position": - double[] cameraProperties = new double[3]; - byte i = 0; - - reader.beginArray(); - while(reader.hasNext()) { - cameraProperties[i] = reader.nextDouble(); - i++; - } + logger.writeln("Deserializing timeline '" + timelineName + "'"); - keyframeProperties.put(nextName, cameraProperties); + // Read timelines + reader.beginArray(); + while(reader.hasNext()) { + List keyframes = new ArrayList<>(); + List segments = new ArrayList<>(); + List interpolators = new ArrayList<>(); + + // Read Paths + reader.beginObject(); + while(reader.hasNext()) { + switch(reader.nextName()) { + case "keyframes": + long time = 0; + Map keyframeProperties; + + // Read Keyframe + reader.beginArray(); + while(reader.hasNext()) { + keyframeProperties = new HashMap<>(); - reader.endArray(); - break; - } - } + reader.beginObject(); + while(reader.hasNext()) { + String nextName = reader.nextName(); - reader.endObject(); + switch(nextName) { + case "time": + time = reader.nextLong(); + break; - break; - } - } + case "properties": + reader.beginObject(); + while(reader.hasNext()) { + String nextNameProperty = reader.nextName(); - keyframes.add(new Keyframe(time, keyframeProperties)); + switch(nextNameProperty) { + case "timestamp": + keyframeProperties.put("timestamp", reader.nextInt()); + break; - reader.endObject(); - } + case "spectate": + keyframeProperties.put("spectate", reader.nextInt()); + break; - reader.endArray(); - break; + case "interpolationFixed": + keyframeProperties.put("interpolationFixed", null); + reader.nextNull(); + break; - case "segments": - reader.beginArray(); - while(reader.hasNext()) { - segments.add(reader.nextInt()); - } + case "camera:rotation": + case "camera:position": + double[] cameraProperties = new double[3]; + byte i = 0; - reader.endArray(); - break; + reader.beginArray(); + while(reader.hasNext()) { + cameraProperties[i] = reader.nextDouble(); + i++; + } - case "interpolators": - reader.beginArray(); - while(reader.hasNext()) { - String type = null; - String alpha = null; - String[] cameraProperties = new String[2]; + keyframeProperties.put(nextNameProperty, cameraProperties); - // Read interpolator - reader.beginObject(); - while(reader.hasNext()) { - switch(reader.nextName()) { - case "type": - try { - reader.beginObject(); - while(reader.hasNext()) { - switch(reader.nextName()) { - case "type": - type = reader.nextString(); + reader.endArray(); break; - case "alpha": - alpha = String.valueOf(reader.nextDouble()); + + default: + logger.writeln("Found unused field in code (keyframe property): " + nextNameProperty); + logger.addUnusedField(nextNameProperty); break; } } reader.endObject(); - } catch(IllegalStateException e) { - type = reader.nextString(); - } - break; - case "properties": - byte i = 0; - - reader.beginArray(); - while(reader.hasNext()) { - cameraProperties[i] = reader.nextString(); - i++; - } - - reader.endArray(); - break; + + break; + + default: + logger.writeln("Found unused field in code (keyframe): " + nextName); + logger.addUnusedField(nextName); + break; + } } + + keyframes.add(new Keyframe(time, keyframeProperties)); + + reader.endObject(); } - interpolators.add(new Interpolator(type, alpha, cameraProperties)); + reader.endArray(); + break; - reader.endObject(); - } + case "segments": + reader.beginArray(); + while(reader.hasNext()) { + segments.add(reader.nextInt()); + } + + reader.endArray(); + break; + + case "interpolators": + reader.beginArray(); + while(reader.hasNext()) { + String type = null; + String alpha = null; + String[] cameraProperties = new String[2]; + + // Read interpolator + reader.beginObject(); + while(reader.hasNext()) { + String nextName = reader.nextName(); + + switch(nextName) { + case "type": + try { + reader.beginObject(); + while(reader.hasNext()) { + String nextNameType = reader.nextName(); + + switch(nextNameType) { + case "type": + type = reader.nextString(); + break; + case "alpha": + alpha = String.valueOf(reader.nextDouble()); + break; + + default: + logger.writeln("Found unused field in code (interpolator type): " + nextNameType); + logger.addUnusedField(nextNameType); + break; + } + } + + reader.endObject(); + } catch(IllegalStateException e) { + type = reader.nextString(); + } + break; + case "properties": + byte i = 0; + + reader.beginArray(); + while(reader.hasNext()) { + cameraProperties[i] = reader.nextString(); + i++; + } + + reader.endArray(); + break; + + default: + logger.writeln("Found unused field in code (interpolator): " + nextName); + logger.addUnusedField(nextName); + break; + } + } + + interpolators.add(new Interpolator(type, alpha, cameraProperties)); - reader.endArray(); - break; + reader.endObject(); + } + + reader.endArray(); + break; + } } + + reader.endObject(); + + paths.add(new Path(keyframes, segments, interpolators)); } - reader.endObject(); + reader.endArray(); - paths.add(new Path(keyframes, segments, interpolators)); + timelines.put(timelineName, new Timeline(paths)); } - reader.endArray(); + reader.endObject(); - timelines.put(timelineName, new Timeline(paths)); + logger.writeln("Done!"); + logger.printUnused(replay); + } catch (Exception exception) { + logger.exception(exception, replay); } - reader.endObject(); - return timelines; } @@ -297,7 +349,7 @@ private String timelineToString(File timelineFile) { return line; } catch (IOException e) { - e.printStackTrace(); + logger.exception(e); } return null;