/** * cemu_UI * * Copyright 2017 <@Seil0> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. */ package application; import java.awt.Desktop; import java.awt.Graphics2D; import java.awt.image.BufferedImage; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.math.BigInteger; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.sql.SQLException; import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.Optional; import java.util.Properties; import javax.imageio.ImageIO; import javax.swing.ProgressMonitor; import javax.swing.ProgressMonitorInputStream; import org.apache.commons.io.FileUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import com.jfoenix.controls.JFXButton; import com.jfoenix.controls.JFXColorPicker; import com.jfoenix.controls.JFXDialog; import com.jfoenix.controls.JFXDialogLayout; import com.jfoenix.controls.JFXHamburger; import com.jfoenix.controls.JFXTextField; import com.jfoenix.controls.JFXToggleButton; import com.jfoenix.controls.JFXTreeTableColumn; import com.jfoenix.controls.JFXTreeTableView; import com.jfoenix.transitions.hamburger.HamburgerBackArrowBasicTransition; import UIElements.JFXInfoDialog; import datatypes.CourseTableDataType; import datatypes.SmmdbApiDataType; import datatypes.UIROMDataType; import javafx.animation.FadeTransition; import javafx.animation.ParallelTransition; import javafx.animation.TranslateTransition; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.fxml.FXML; import javafx.geometry.Insets; import javafx.scene.control.Alert; import javafx.scene.control.Alert.AlertType; import javafx.scene.control.Button; import javafx.scene.control.ButtonBar.ButtonData; import javafx.scene.control.ButtonType; import javafx.scene.control.ChoiceBox; import javafx.scene.control.ContextMenu; import javafx.scene.control.Dialog; import javafx.scene.control.Label; import javafx.scene.control.MenuItem; import javafx.scene.control.ScrollPane; import javafx.scene.control.TextField; import javafx.scene.control.TreeItem; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.input.MouseButton; import javafx.scene.input.MouseEvent; import javafx.scene.layout.AnchorPane; import javafx.scene.layout.GridPane; import javafx.scene.layout.HBox; import javafx.scene.layout.StackPane; import javafx.scene.layout.VBox; import javafx.scene.paint.Color; import javafx.scene.paint.Paint; import javafx.scene.text.Font; import javafx.scene.text.FontWeight; import javafx.scene.text.Text; import javafx.scene.text.TextFlow; import javafx.stage.DirectoryChooser; import javafx.stage.FileChooser; import javafx.util.Duration; import net.lingala.zip4j.core.ZipFile; import net.lingala.zip4j.exception.ZipException; public class MainWindowController { @FXML private JFXButton aboutBtn; @FXML private JFXButton settingsBtn; @FXML private JFXButton addBtn; @FXML private JFXButton reloadRomsBtn; @FXML private JFXButton smmdbBtn; @FXML private JFXButton cemuTFBtn; @FXML private JFXButton romTFBtn; @FXML private JFXButton updateBtn; @FXML private JFXButton smmdbDownloadBtn; @FXML private JFXButton playBtn; @FXML private JFXButton lastTimePlayedBtn; @FXML JFXButton totalPlaytimeBtn; @FXML private JFXHamburger menuHam; @FXML private JFXTextField cemuTextField; @FXML private JFXTextField romTextField; @FXML private TextFlow smmdbTextFlow; @FXML private JFXColorPicker colorPicker; @FXML private JFXToggleButton cloudSyncToggleBtn; @FXML private JFXToggleButton autoUpdateToggleBtn; @FXML private JFXToggleButton fullscreenToggleBtn; @FXML private ChoiceBox branchChoisBox; @FXML private AnchorPane mainAnchorPane; @FXML private AnchorPane gamesAnchorPane; @FXML private AnchorPane settingsAnchorPane; @FXML private AnchorPane smmdbAnchorPane; @FXML private ScrollPane mainScrollPane; @FXML private ScrollPane settingsScrollPane; @FXML private ScrollPane smmdbScrollPane; @FXML private VBox sideMenuVBox; @FXML private HBox topHBox; @FXML private ImageView smmdbImageView; @FXML private Label helpLabel; @FXML private Label cemu_UISettingsLbl; @FXML private Label cemuDirectoryLbl; @FXML private Label romDirectoryLbl; @FXML private Label mainColorLbl; @FXML private Label updateLbl; @FXML private Label branchLbl; @FXML private Label cemuSettingsLbl; @FXML private JFXTreeTableView courseTreeTable = new JFXTreeTableView(); @FXML TreeItem root = new TreeItem<>(new CourseTableDataType("","",0,0)); @FXML private JFXTreeTableColumn titleColumn = new JFXTreeTableColumn<>("title"); @FXML private JFXTreeTableColumn idColumn = new JFXTreeTableColumn<>("id"); @FXML private JFXTreeTableColumn starsColumn = new JFXTreeTableColumn<>("stars"); @FXML private JFXTreeTableColumn timeColumn = new JFXTreeTableColumn<>("time"); Main main; dbController dbController; SmmdbApiQuery smmdbApiQuery; playGame playGame; private UpdateController updateController; private boolean menuTrue = false; private boolean settingsTrue = false; private boolean playTrue = false; private boolean smmdbTrue = false; private boolean autoUpdate = false; private boolean useBeta = false; private boolean fullscreen; private boolean cloudSync; private String cloudService = ""; //set cloud provider (at the moment only GoogleDrive, Dropbox is planed) private String cemuPath; private String romPath; private String gameExecutePath; private String color; private String dialogBtnStyle; private String selectedGameTitleID; private String selectedGameTitle; private String id; private String version = "0.2.0"; private String buildNumber = "047"; private String versionName = "Puzzle Plank Galaxy"; private int xPos = -200; private int yPos = 17; private int xPosHelper; private int oldXPosHelper; private int selectedUIDataIndex; private int selected; private double windowWidth; private double windowHeight; private DirectoryChooser directoryChooser = new DirectoryChooser(); private File dirWin = new File(System.getProperty("user.home") + "/Documents/cemu_UI"); private File dirLinux = new File(System.getProperty("user.home") + "/cemu_UI"); private File configFileWin = new File(dirWin + "/config.xml"); private File configFileLinux = new File(dirLinux + "/config.xml"); File pictureCacheWin = new File(dirWin+"/picture_cache"); File pictureCacheLinux = new File(dirLinux+"/picture_cache"); private ObservableList branches = FXCollections.observableArrayList("stable", "beta"); private ObservableList smmIDs = FXCollections.observableArrayList("fe31b7f2", "44fc5929"); //TODO add more IDs private ObservableList games = FXCollections.observableArrayList(); ObservableList courses = FXCollections.observableArrayList(); ArrayList courseText = new ArrayList(); ArrayList nameText = new ArrayList(); Properties props = new Properties(); Properties gameProps = new Properties(); private static final Logger LOGGER = LogManager.getLogger(MainWindowController.class.getName()); private HamburgerBackArrowBasicTransition burgerTask; private MenuItem edit = new MenuItem("edit"); private MenuItem remove = new MenuItem("remove"); private MenuItem update = new MenuItem("update"); private MenuItem addDLC = new MenuItem("add DLC"); private ContextMenu gameContextMenu = new ContextMenu(edit, remove, update, addDLC); private Label lastGameLabel = new Label(); private ImageView add_circle_black = new ImageView(new Image("resources/icons/ic_add_circle_black_24dp_1x.png")); private ImageView info_black = new ImageView(new Image("resources/icons/ic_info_black_24dp_1x.png")); private ImageView settings_black = new ImageView(new Image("resources/icons/ic_settings_black_24dp_1x.png")); private ImageView cached_black = new ImageView(new Image("resources/icons/ic_cached_black_24dp_1x.png")); private ImageView smmdb_black = new ImageView(new Image("resources/icons/ic_get_app_black_24dp_1x.png")); private ImageView add_circle_white = new ImageView(new Image("resources/icons/ic_add_circle_white_24dp_1x.png")); private ImageView info_white = new ImageView(new Image("resources/icons/ic_info_white_24dp_1x.png")); private ImageView settings_white = new ImageView(new Image("resources/icons/ic_settings_white_24dp_1x.png")); private ImageView cached_white = new ImageView(new Image("resources/icons/ic_cached_white_24dp_1x.png")); private ImageView smmdb_white = new ImageView(new Image("resources/icons/ic_get_app_white_24dp_1x.png")); private Image close_black = new Image("resources/icons/close_black_2048x2048.png"); public void setMain(Main main) { this.main = main; dbController = new dbController(this); smmdbApiQuery = new SmmdbApiQuery(); } void initUI() { LOGGER.info("initializing UI ..."); if (getWindowWidth() > 100 && getWindowHeight() > 100) { mainAnchorPane.setPrefSize(getWindowWidth(), getWindowHeight()); } refreshplayBtnPosition(); cemuTextField.setText(cemuPath); romTextField.setText(romPath); colorPicker.setValue(Color.valueOf(getColor())); fullscreenToggleBtn.setSelected(isFullscreen()); cloudSyncToggleBtn.setSelected(isCloudSync()); autoUpdateToggleBtn.setSelected(isAutoUpdate()); branchChoisBox.setItems(branches); if (isUseBeta()) { branchChoisBox.getSelectionModel().select(1); } else { branchChoisBox.getSelectionModel().select(0); } applyColor(); //initialize courseTable titleColumn.setPrefWidth(160); timeColumn.setPrefWidth(127); starsColumn.setPrefWidth(100); courseTreeTable.setRoot(root); courseTreeTable.setShowRoot(false); courseTreeTable.setEditable(false); titleColumn.setCellValueFactory(cellData -> cellData.getValue().getValue().title); idColumn.setCellValueFactory(cellData -> cellData.getValue().getValue().id); starsColumn.setCellValueFactory(cellData -> cellData.getValue().getValue().stars.asObject()); timeColumn.setCellValueFactory(cellData -> cellData.getValue().getValue().time.asObject()); courseTreeTable.getColumns().add(titleColumn); courseTreeTable.getColumns().add(timeColumn); courseTreeTable.getColumns().add(starsColumn); courseTreeTable.getColumns().add(idColumn); courseTreeTable.getColumns().get(3).setVisible(false); //hide idColumn (important) LOGGER.info("initializing UI done"); } /** * initialize all actions not initialized by a own method */ void initActions() { LOGGER.info("initializing Actions ..."); burgerTask = new HamburgerBackArrowBasicTransition(menuHam); menuHam.addEventHandler(MouseEvent.MOUSE_PRESSED, (e)->{ if (playTrue) { playBtnSlideOut(); } if (menuTrue) { sideMenuSlideOut(); burgerTask.setRate(-1.0); burgerTask.play(); menuTrue = false; } else { sideMenuSlideIn(); burgerTask.setRate(1.0); burgerTask.play(); menuTrue = true; } if (settingsTrue) { settingsScrollPane.setVisible(false); // setPath(tfPath.getText()); saveSettings(); settingsTrue = false; } if (smmdbTrue) { smmdbAnchorPane.setVisible(false); smmdbTrue = false; } }); edit.setOnAction(new EventHandler() { @Override public void handle(ActionEvent event) { LOGGER.info("edit "+selectedGameTitleID); if(selectedGameTitleID == null){ LOGGER.warn("trying to edit null! null is not valid!"); Alert alert = new Alert(AlertType.WARNING); alert.setTitle("edit"); alert.setHeaderText("cemu_UI"); alert.setContentText("please select a game, \""+selectedGameTitleID+"\" is not a valid type"); alert.initOwner(main.primaryStage); alert.showAndWait(); }else{ String[] gameInfo = dbController.getGameInfo(selectedGameTitleID); //new Dialog Dialog dialog = new Dialog<>(); dialog.setTitle("edit game"); dialog.setHeaderText("You can edit the tile and rom/cover path."); // Set the button types. ButtonType okayBtn = new ButtonType("Okay", ButtonData.OK_DONE); dialog.getDialogPane().getButtonTypes().addAll(okayBtn, ButtonType.CANCEL); // Create gameTitle, titleID, gamePath and gameCover TextFields and Labels and two Btn for filechooser GridPane grid = new GridPane(); grid.setHgap(10); grid.setVgap(10); grid.setPadding(new Insets(20, 150, 10, 10)); TextField gameTitleTF = new TextField(); gameTitleTF.setPromptText("game tile"); TextField titleIDTF = new TextField(); titleIDTF.setPromptText("title ID"); TextField romPathTF = new TextField(); romPathTF.setPromptText("ROM path"); TextField gameCoverTF = new TextField(); gameCoverTF.setPromptText("cover path"); gameTitleTF.setText(gameInfo[0]); titleIDTF.setText(gameInfo[3]); romPathTF.setText(gameInfo[2]); gameCoverTF.setText(gameInfo[1]); titleIDTF.setEditable(false); Button selectPathBtn = new Button("select .rpx file"); Button selectCoverBtn = new Button("select cover file"); selectPathBtn.setPrefWidth(110); selectCoverBtn.setPrefWidth(110); selectPathBtn.setOnAction(new EventHandler() { @Override public void handle(ActionEvent event) { FileChooser romDirectoryChooser = new FileChooser(); File romDirectory = romDirectoryChooser.showOpenDialog(main.primaryStage); romPathTF.setText(romDirectory.getAbsolutePath()); } }); selectCoverBtn.setOnAction(new EventHandler() { @Override public void handle(ActionEvent event) { FileChooser coverDirectoryChooser = new FileChooser(); File coverDirectory = coverDirectoryChooser.showOpenDialog(main.primaryStage); gameCoverTF.setText(coverDirectory.getAbsolutePath()); } }); grid.add(new Label("game title:"), 0, 0); grid.add(gameTitleTF, 1, 0); grid.add(new Label("title id:"), 0, 1); grid.add(titleIDTF, 1, 1); grid.add(new Label("ROM path:"), 0, 2); grid.add(romPathTF, 1, 2); grid.add(selectPathBtn, 2, 2); grid.add(new Label("cover path:"), 0, 3); grid.add(gameCoverTF, 1, 3); grid.add(selectCoverBtn, 2, 3); dialog.getDialogPane().setContent(grid); Optional result2 = dialog.showAndWait(); if (result2.isPresent()){ dbController.setGameInfo(gameTitleTF.getText(), gameInfo[3], romPathTF.getText(), gameCoverTF.getText()); games.remove(selectedUIDataIndex); dbController.loadSingleRom(gameInfo[3]); refreshUIData(); LOGGER.info("successfully edited \"" + gameInfo[0] + "\", new name is \"" + gameTitleTF.getText() + "\""); } } } }); remove.setOnAction(new EventHandler() { @Override public void handle(ActionEvent event) { LOGGER.info("remove "+selectedGameTitleID); if(selectedGameTitleID == null){ LOGGER.warn("trying to remove null! null is not valid!"); Alert alert = new Alert(AlertType.WARNING); alert.setTitle("remove"); alert.setHeaderText("cemu_UI"); alert.setContentText("please select a game, \""+selectedGameTitleID+"\" is not a valid type"); alert.initOwner(main.primaryStage); alert.showAndWait(); } else{ Alert alert = new Alert(AlertType.CONFIRMATION); alert.setTitle("remove"); alert.setHeaderText("cemu_UI"); alert.setContentText("Are you sure you want to delete "+selectedGameTitle+" ?"); alert.initOwner(main.primaryStage); Optional result = alert.showAndWait(); if (result.get() == ButtonType.OK){ try { //remove game from database games.remove(selectedUIDataIndex); dbController.removeRom(selectedGameTitleID); //refresh all games at gamesAnchorPane (UI) refreshUIData(); } catch (Exception e) { LOGGER.error("error!",e); } } } } }); update.setOnAction(new EventHandler() { @Override public void handle(ActionEvent event) { String titleID = selectedGameTitleID; String updatePath; LOGGER.info("update: "+selectedGameTitleID); if(selectedGameTitleID == null){ LOGGER.warn("trying to update null! null is not valid!"); Alert alert = new Alert(AlertType.WARNING); alert.setTitle("edit"); alert.setHeaderText("cemu_UI"); alert.setContentText("please select a game, \""+selectedGameTitleID+"\" is not a valid type"); alert.initOwner(main.primaryStage); alert.showAndWait(); }else{ Alert updateAlert = new Alert(AlertType.CONFIRMATION); //new alert with file-chooser updateAlert.setTitle("cemu_UI"); updateAlert.setHeaderText("update "+selectedGameTitle); updateAlert.setContentText("pleas select the update root directory"); updateAlert.initOwner(main.primaryStage); Optional result = updateAlert.showAndWait(); if (result.get() == ButtonType.OK){ DirectoryChooser directoryChooser = new DirectoryChooser(); File selectedDirecroty = directoryChooser.showDialog(main.primaryStage); updatePath = selectedDirecroty.getAbsolutePath(); String[] parts = titleID.split("-"); //split string into 2 parts at "-" File srcDir = new File(updatePath); File destDir = new File(cemuPath+"\\mlc01\\usr\\title\\"+parts[0]+"\\"+parts[1]); LOGGER.info(updatePath); LOGGER.info(destDir.toString()); if(destDir.exists() != true){ destDir.mkdir(); } try { LOGGER.info("copying files..."); playBtn.setText("updating..."); playBtn.setDisable(true); FileUtils.copyDirectory(srcDir, destDir); //TODO progress indicator playBtn.setText("play"); playBtn.setDisable(false); LOGGER.info("copying files done!"); } catch (IOException e) { e.printStackTrace(); } } else { updatePath = null; } } } }); addDLC.setOnAction(new EventHandler() { @Override public void handle(ActionEvent event) { String titleID = selectedGameTitleID; String dlcPath; LOGGER.info("add DLC: "+selectedGameTitleID); if(selectedGameTitleID == null){ LOGGER.warn("trying to add a dlc to null! null is not valid!"); Alert alert = new Alert(AlertType.WARNING); alert.setTitle("add DLC"); alert.setHeaderText("cemu_UI"); alert.setContentText("please select a game, \""+selectedGameTitleID+"\" is not a valid type"); alert.initOwner(main.primaryStage); alert.showAndWait(); }else{ Alert updateAlert = new Alert(AlertType.CONFIRMATION); //new alert with file-chooser updateAlert.setTitle("cemu_UI"); updateAlert.setHeaderText("add a DLC to "+selectedGameTitle); updateAlert.setContentText("pleas select the DLC root directory"); updateAlert.initOwner(main.primaryStage); Optional result = updateAlert.showAndWait(); if (result.get() == ButtonType.OK){ DirectoryChooser directoryChooser = new DirectoryChooser(); File selectedDirecroty = directoryChooser.showDialog(main.primaryStage); dlcPath = selectedDirecroty.getAbsolutePath(); String[] parts = titleID.split("-"); //split string into 2 parts at "-" File srcDir = new File(dlcPath); File destDir = new File(cemuPath+"\\mlc01\\usr\\title\\"+parts[0]+"\\"+parts[1]+"\\aoc"); LOGGER.info(dlcPath); LOGGER.info(destDir.toString()); if(destDir.exists() != true){ destDir.mkdir(); } try { LOGGER.info("copying files..."); playBtn.setText("copying files..."); playBtn.setDisable(true); FileUtils.copyDirectory(srcDir, destDir); //TODO progress indicator playBtn.setText("play"); playBtn.setDisable(false); LOGGER.info("copying files done!"); } catch (IOException e) { e.printStackTrace(); } } else { dlcPath = null; } } } }); gamesAnchorPane.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler() { @Override public void handle(MouseEvent event) { if (playTrue) { playBtnSlideOut(); } } }); topHBox.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler() { @Override public void handle(MouseEvent event) { if (playTrue) { playBtnSlideOut(); } } }); //Change-listener for TreeTable courseTreeTable.getSelectionModel().selectedItemProperty().addListener(new ChangeListener() { @Override public void changed(ObservableValue observable, Object oldVal, Object newVal){ selected = courseTreeTable.getSelectionModel().getSelectedIndex(); //get selected item //FIXME if a item is selected and you change the sorting,you can't select a new item id = idColumn.getCellData(selected); //get name of selected item for (int i = 0; i < courses.size(); i++) { if (courses.get(i).getId() == id) { try { URL url = new URL("http://smmdb.ddns.net/courseimg/" + id + "_full.jpg?v=3"); Image image = new Image(url.toURI().toString()); smmdbImageView.setImage(image); } catch (MalformedURLException | URISyntaxException e) { e.printStackTrace(); smmdbImageView.setImage(close_black); } addCourseDescription(courses.get(i)); } } } }); helpLabel.setOnMouseClicked(new EventHandler() { @Override public void handle(MouseEvent mouseEvent) { if(mouseEvent.getButton().equals(MouseButton.PRIMARY)){ try { Desktop.getDesktop().browse(new URI("https://github.com/Seil0/cemu_UI/issues/3")); } catch (IOException | URISyntaxException e) { e.printStackTrace(); } } } }); branchChoisBox.getSelectionModel().selectedIndexProperty().addListener(new ChangeListener() { @Override public void changed(ObservableValue ov, Number value, Number new_value) { if (branchChoisBox.getItems().get((int) new_value).toString() == "beta") { setUseBeta(true); } else { setUseBeta(false); } saveSettings(); } }); LOGGER.info("initializing Actions done!"); } @FXML void detailsSlideoutBtnAction(ActionEvent event){ playBtnSlideOut(); } @FXML void aboutBtnAction(){ String headingText = "cemu_UI"; String bodyText = "cemu_UI by @Seil0 \nVersion: " + version + " (" + buildNumber + ") \"" + versionName + "\" \n" + "This Application is made with free Software\n" + "www.kellerkinder.xyz"; JFXInfoDialog aboutDialog = new JFXInfoDialog(headingText, bodyText, dialogBtnStyle, 350, 170, main.pane); aboutDialog.show(); } @FXML void settingsBtnAction(ActionEvent event) { if (smmdbTrue) { smmdbAnchorPane.setVisible(false); smmdbTrue = false; } if (settingsTrue) { settingsScrollPane.setVisible(false); settingsTrue = false; saveSettings(); } else { settingsScrollPane.setVisible(true); settingsTrue = true; } } @FXML void reloadRomsBtnAction() throws IOException{ reloadRomsBtn.setText("reloading..."); dbController.loadRomDirectory(getRomPath()); //TODO own thread Runtime.getRuntime().exec("java -jar cemu_UI.jar"); //start again (preventing Bugs) System.exit(0); //finishes itself } @FXML void smmdbBtnAction() { //show smmdbAnchorPane if (smmdbTrue) { smmdbAnchorPane.setVisible(false); smmdbTrue = false; } else { smmdbAnchorPane.setVisible(true); smmdbTrue = true; //start query courses.removeAll(courses); courses.addAll(smmdbApiQuery.startQuery()); //add query response to courseTreeTable for(int i = 0; i < courses.size(); i++){ CourseTableDataType helpCourse = new CourseTableDataType(courses.get(i).getTitle(), courses.get(i).getId(), courses.get(i).getTime(), courses.get(i).getStars()); root.getChildren().add(new TreeItem(helpCourse)); //add data to root-node } } } @FXML void playBtnAction(ActionEvent event) throws InterruptedException, IOException{ dbController.setLastPlayed(selectedGameTitleID); playGame = new playGame(this,dbController); playGame.start(); } @FXML void totalPlaytimeBtnAction(ActionEvent event){ } @FXML void lastTimePlayedBtnAction(ActionEvent event){ } @FXML void cemuTFBtnAction(ActionEvent event) { File cemuDirectory = directoryChooser.showDialog(main.primaryStage); if(cemuDirectory == null){ LOGGER.info("No Directory selected"); }else{ setCemuPath(cemuDirectory.getAbsolutePath()); saveSettings(); cemuTextField.setText(getCemuPath()); try { Runtime.getRuntime().exec("java -jar cemu_UI.jar"); //start again System.exit(0); //finishes itself } catch (IOException e) { LOGGER.error("an error occurred", e); } } } @FXML void romTFBtnAction(ActionEvent event) { File romDirectory = directoryChooser.showDialog(main.primaryStage); if(romDirectory == null){ LOGGER.info("No Directory selected"); }else{ setRomPath(romDirectory.getAbsolutePath()); saveSettings(); cemuTextField.setText(getCemuPath()); try { Runtime.getRuntime().exec("java -jar cemu_UI.jar"); //start again System.exit(0); //finishes itself } catch (IOException e) { LOGGER.error("an error occurred", e); } } } @FXML void updateBtnAction(ActionEvent event) { updateController = new UpdateController(this, buildNumber, useBeta); Thread updateThread = new Thread(updateController); updateThread.setName("Updater"); updateThread.start(); } @FXML void autoUpdateToggleBtnAction(ActionEvent event) { if(isAutoUpdate()){ setAutoUpdate(false); }else{ setAutoUpdate(true); } saveSettings(); } @FXML void smmdbDownloadBtnAction(ActionEvent event){ String downloadUrl = "http://smmdb.ddns.net/api/downloadcourse?id=" + id + "&type=zip"; String downloadFileURL = getCemuPath() + "/" + id + ".zip"; //getCemuPath() + "/" + smmID + "/" + id + ".rar" String outputFile = getCemuPath() + "/"; try { HttpURLConnection conn = (HttpURLConnection) new URL(downloadUrl).openConnection(); ProgressMonitorInputStream pmis = new ProgressMonitorInputStream(null, "Downloading...", conn.getInputStream()); ProgressMonitor pm = pmis.getProgressMonitor(); pm.setMillisToDecideToPopup(0); pm.setMillisToPopup(0); pm.setMinimum(0); // tell the progress bar that we start at the beginning of the stream pm.setMaximum(conn.getContentLength()); // tell the progress bar the total number of bytes we are going to read. FileUtils.copyInputStreamToFile(pmis, new File(downloadFileURL)); //download file + "/mlc01/emulatorSave" pmis.close(); LOGGER.info("downloaded successfull"); File downloadFile = new File(downloadFileURL); String source = downloadFileURL; String destination = null; int highestCourseNumber = 0; String courseName = null; for (int i = 0; i < smmIDs.size(); i++) { File smmDirectory = new File(outputFile + "mlc01/emulatorSave/" + smmIDs.get(i)); if (smmDirectory.exists()) { File[] courses = smmDirectory.listFiles(File::isDirectory); //get all existing courses in smm directory, new name is highest number +1 for (int j = 0; j < courses.length; j++) { int courseNumber = Integer.parseInt(courses[j].getName().substring(6)); if (courseNumber > highestCourseNumber) { highestCourseNumber = courseNumber; } } String number = "000" + (highestCourseNumber +1); courseName = "course" + number.substring(number.length() -3, number.length()); File courseDirectory = new File(outputFile + "mlc01/emulatorSave/" + smmIDs.get(i) + "/"); destination = courseDirectory.getPath(); } } try { ZipFile zipFile = new ZipFile(source); zipFile.extractAll(destination); //rename zipfile File course = new File(destination + "/course000"); course.renameTo( new File(destination + "/" + courseName)); LOGGER.info("Added new course: " + courseName + ", full path is: " + destination + "/" + courseName); } catch (ZipException e) { LOGGER.error("an error occurred during unziping the file!", e); } downloadFile.delete(); } catch (IOException e) { LOGGER.error("something went wrong during downloading the course", e); } } @FXML void cemuTextFieldAction(ActionEvent event){ setCemuPath(cemuTextField.getText()); saveSettings(); } @FXML void romTextFieldAction(ActionEvent event){ setRomPath(romTextField.getText()); saveSettings(); } @FXML void fullscreenToggleBtnAction(ActionEvent event){ if(fullscreen){ fullscreen = false; }else{ fullscreen = true; } saveSettings(); } @FXML void cloudSyncToggleBtnAction(ActionEvent event){ if(cloudSync) { cloudSync = false; } else { JFXDialogLayout content= new JFXDialogLayout(); content.setHeading(new Text("activate cloud savegame sync (beta)")); content.setBody(new Text("You just activate the cloud savegame sync function of cemu_UI, \nwhich is currently in beta. Are you sure you want to do this?")); StackPane stackPane = new StackPane(); stackPane.autosize(); JFXDialog betaDialog = new JFXDialog(stackPane, content, JFXDialog.DialogTransition.LEFT, true); JFXButton okayBtn = new JFXButton("Okay"); okayBtn.setOnAction(new EventHandler(){ @Override public void handle(ActionEvent event){ cloudSync = true; //TODO rework for other cloud services cloudService = "GoogleDrive"; if (main.cloudController.initializeConnection(getCloudService(), getCemuPath())) { main.cloudController.sync(getCloudService(), getCemuPath()); saveSettings(); } else { cloudSyncToggleBtn.setSelected(false); //cloud sync init error dialog JFXDialogLayout content= new JFXDialogLayout(); content.setHeading(new Text("Error while initializing cloud sync!")); content.setBody(new Text("There was some truble initializing the cloud sync." + "\nPlease upload the app.log (which can be found in the cemu_UI directory)" + "\nto \"https://github.com/Seil0/cemu_UI/issues\" so we can fix this.")); content.setPrefSize(450, 170); StackPane stackPane = new StackPane(); stackPane.autosize(); JFXDialog dialog =new JFXDialog(stackPane, content, JFXDialog.DialogTransition.LEFT, true); JFXButton button=new JFXButton("Okay"); button.setOnAction(new EventHandler(){ @Override public void handle(ActionEvent event){ dialog.close(); } }); button.setButtonType(com.jfoenix.controls.JFXButton.ButtonType.RAISED); button.setPrefHeight(32); button.setStyle(dialogBtnStyle); content.setActions(button); main.pane.getChildren().add(stackPane); AnchorPane.setTopAnchor(stackPane, (main.pane.getHeight()-content.getPrefHeight())/2); AnchorPane.setLeftAnchor(stackPane, (main.pane.getWidth()-content.getPrefWidth())/2); dialog.show(); } betaDialog.close(); } }); JFXButton cancelBtn = new JFXButton("Cancel"); cancelBtn.setOnAction(new EventHandler(){ @Override public void handle(ActionEvent event){ cloudSyncToggleBtn.setSelected(false); betaDialog.close(); } }); Label placeholder = new Label(); placeholder.setPrefSize(15, 10); okayBtn.setButtonType(com.jfoenix.controls.JFXButton.ButtonType.RAISED); cancelBtn.setButtonType(com.jfoenix.controls.JFXButton.ButtonType.RAISED); okayBtn.setPrefHeight(32); cancelBtn.setPrefHeight(32); okayBtn.setStyle(dialogBtnStyle); cancelBtn.setStyle(dialogBtnStyle); content.setActions(cancelBtn, placeholder, okayBtn); content.setPrefSize(419, 140); main.pane.getChildren().add(stackPane); AnchorPane.setTopAnchor(stackPane, (main.pane.getHeight()-content.getPrefHeight())/2); AnchorPane.setLeftAnchor(stackPane, (main.pane.getWidth()-content.getPrefWidth())/2); betaDialog.show(); } } @FXML void colorPickerAction(ActionEvent event){ editColor(colorPicker.getValue().toString()); applyColor(); } @FXML void addBtnAction(ActionEvent event){ String romPath = null; String coverPath = null; String coverName = null; String title = null; String titleID = null; File pictureCache; //new Dialog Dialog dialog = new Dialog<>(); dialog.setTitle("add a new game"); dialog.setHeaderText("add a new game manually to cemu UI"); // Set the button types. ButtonType okayBtn = new ButtonType("Okay", ButtonData.OK_DONE); dialog.getDialogPane().getButtonTypes().addAll(okayBtn, ButtonType.CANCEL); // Create gameTitle, titleID, gamePath and gameCover TextFields and Labels and two Btn for filechooser GridPane grid = new GridPane(); grid.setHgap(10); grid.setVgap(10); grid.setPadding(new Insets(20, 150, 10, 10)); TextField gameTitleTF = new TextField(); gameTitleTF.setPromptText("game tile"); TextField titleIDTF = new TextField(); titleIDTF.setPromptText("title ID"); TextField romPathTF = new TextField(); romPathTF.setPromptText("ROM path"); TextField gameCoverTF = new TextField(); gameCoverTF.setPromptText("cover path"); Button selectPathBtn = new Button("select .rpx file"); Button selectCoverBtn = new Button("select cover file"); selectPathBtn.setPrefWidth(110); selectCoverBtn.setPrefWidth(110); selectPathBtn.setOnAction(new EventHandler() { @Override public void handle(ActionEvent event) { FileChooser romDirectoryChooser = new FileChooser(); File romDirectory = romDirectoryChooser.showOpenDialog(main.primaryStage); romPathTF.setText(romDirectory.getAbsolutePath()); } }); selectCoverBtn.setOnAction(new EventHandler() { @Override public void handle(ActionEvent event) { FileChooser coverDirectoryChooser = new FileChooser(); File coverDirectory = coverDirectoryChooser.showOpenDialog(main.primaryStage); gameCoverTF.setText(coverDirectory.getAbsolutePath()); } }); grid.add(new Label("game title:"), 0, 0); grid.add(gameTitleTF, 1, 0); grid.add(new Label("title id:"), 0, 1); grid.add(titleIDTF, 1, 1); grid.add(new Label("ROM path:"), 0, 2); grid.add(romPathTF, 1, 2); grid.add(selectPathBtn, 2, 2); grid.add(new Label("cover path:"), 0, 3); grid.add(gameCoverTF, 1, 3); grid.add(selectCoverBtn, 2, 3); dialog.getDialogPane().setContent(grid); Optional result2 = dialog.showAndWait(); if (result2.isPresent()){ romPath = romPathTF.getText(); coverPath = gameCoverTF.getText(); title = gameTitleTF.getText(); titleID = titleIDTF.getText(); LOGGER.info("New game data \"" + title + "\", title-ID: " + titleID + "(not a bug!)"); } /** * if one parameter dosen't contain any value do not add the game * else convert the cover to .png add copy it into the picture cache, * then add the rom to the local_roms database */ System.out.println(romPath.length()); if (romPath.length() == 0 || coverPath.length() == 0 || title.length() == 0 || titleID.length() == 0) { LOGGER.info("No parameter set!"); //addGame error dialog JFXDialogLayout content= new JFXDialogLayout(); content.setHeading(new Text("Error while adding a new Game!")); content.setBody(new Text("There was some truble adding your game." + "\nOne of the needed values was empty, please try again to add your game.")); content.setPrefSize(450, 170); StackPane stackPane = new StackPane(); stackPane.autosize(); JFXDialog errorDialog =new JFXDialog(stackPane, content, JFXDialog.DialogTransition.LEFT, true); JFXButton button=new JFXButton("Okay"); button.setOnAction(new EventHandler(){ @Override public void handle(ActionEvent event){ errorDialog.close(); } }); button.setButtonType(com.jfoenix.controls.JFXButton.ButtonType.RAISED); button.setPrefHeight(32); button.setStyle(dialogBtnStyle); content.setActions(button); main.pane.getChildren().add(stackPane); AnchorPane.setTopAnchor(stackPane, (main.pane.getHeight()-content.getPrefHeight())/2); AnchorPane.setLeftAnchor(stackPane, (main.pane.getWidth()-content.getPrefWidth())/2); errorDialog.show(); } else { coverName = new File(coverPath).getName(); try { if (System.getProperty("os.name").equals("Linux")) { pictureCache = pictureCacheLinux; } else { pictureCache = pictureCacheWin; } BufferedImage originalImage = ImageIO.read(new File(coverPath)); //load cover int type = originalImage.getType() == 0 ? BufferedImage.TYPE_INT_ARGB : originalImage.getType(); BufferedImage resizeImagePNG = resizeImage(originalImage, type, 400, 600); ImageIO.write(resizeImagePNG, "png", new File(pictureCache+"\\"+coverName)); //save image to pictureCache coverPath = pictureCache+"\\"+coverName; } catch (IOException e) { LOGGER.error("Ops something went wrong! Error while resizing cover.", e); } try { dbController.addRom(title, coverPath, romPath, titleID, "", "", "", "0"); dbController.loadSingleRom(titleID); refreshUIData(); } catch (SQLException e) { LOGGER.error("Oops, something went wrong! Error while adding a game.", e); } } } /** * add game to games (ArrayList) and initialize all needed actions (start, time stamps, titleID) * @param title : game title * @param coverPath : path to cover (cache) * @param romPath : path to ROM file (.rpx) * @param titleID : ROM ID */ void addGame(String title, String coverPath, String romPath, String titleID){ VBox VBox = new VBox(); Label gameTitleLabel = new Label(); JFXButton gameBtn = new JFXButton(); ImageView imageView = new ImageView(); Image coverImage = new Image(new File(coverPath).toURI().toString()); generatePosition(); UIROMDataType uiROMElement = new UIROMDataType(VBox, gameTitleLabel, gameBtn, imageView, titleID, romPath); uiROMElement.getLabel().setText(title); uiROMElement.getLabel().setMaxWidth(200); uiROMElement.getLabel().setPadding(new Insets(0,0,0,8)); uiROMElement.getLabel().setFont(Font.font("System", FontWeight.BOLD, 14)); // i think we can do this locally and remove the imageView from the data type since it's used as graphic uiROMElement.getImageView().setImage(coverImage); uiROMElement.getImageView().setFitHeight(300); uiROMElement.getImageView().setFitWidth(200); uiROMElement.getButton().setGraphic(uiROMElement.getImageView()); uiROMElement.getButton().setContextMenu(gameContextMenu); uiROMElement.getButton().setStyle("-fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.8), 10, 0, 0, 3); "); uiROMElement.getButton().addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler() { @Override public void handle(MouseEvent event) { LOGGER.info("selected: " + title + "; ID: " + titleID); // getting the selected game index by comparing event.getSource() with games.get(i).getButton() for (int i = 0; i < games.size(); i++) { if (games.get(i).getButton() == event.getSource()) { selectedUIDataIndex = i; } } gameExecutePath = romPath; selectedGameTitleID = titleID; selectedGameTitle = title; // underlining selected Label lastGameLabel.setStyle("-fx-underline: false;"); games.get(selectedUIDataIndex).getLabel().setStyle("-fx-underline: true;"); lastGameLabel = games.get(selectedUIDataIndex).getLabel(); // setting last played, if lastPlayed is empty game was never played before, else set correct date if (dbController.getLastPlayed(titleID).equals("") || dbController.getLastPlayed(titleID).equals(null)) { lastTimePlayedBtn.setText("Last played, never"); totalPlaytimeBtn.setText(dbController.getTotalPlaytime(titleID) + " min"); } else { DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd"); int today = Integer.parseInt(dtf.format(LocalDate.now()).replaceAll("-", "")); int yesterday = Integer.parseInt(dtf.format(LocalDate.now().minusDays(1)).replaceAll("-", "")); int lastPlayedDay = Integer.parseInt(dbController.getLastPlayed(titleID).replaceAll("-", "")); if (lastPlayedDay == today) { lastTimePlayedBtn.setText("Last played, today"); } else if (lastPlayedDay == yesterday) { lastTimePlayedBtn.setText("Last played, yesterday"); } else { lastTimePlayedBtn.setText("Last played, " + dbController.getLastPlayed(titleID)); } } // setting total playtime, if total playtime > 60 minutes, format is "x hours x minutes" (x h x min), // else only minutes are showed if (Integer.parseInt(dbController.getTotalPlaytime(titleID)) > 60) { int hoursPlayed = (int) Math.floor(Integer.parseInt(dbController.getTotalPlaytime(titleID)) / 60); int minutesPlayed = Integer.parseInt(dbController.getTotalPlaytime(titleID)) - 60 * hoursPlayed; totalPlaytimeBtn.setText(hoursPlayed + " h " + minutesPlayed + " min"); } else { totalPlaytimeBtn.setText(dbController.getTotalPlaytime(titleID) + " min"); } if (!playTrue) { playBtnSlideIn(); } if (menuTrue) { sideMenuSlideOut(); burgerTask.setRate(-1.0); burgerTask.play(); menuTrue = false; } } }); uiROMElement.getVBox().setLayoutX(getxPos()); uiROMElement.getVBox().setLayoutY(getyPos()); uiROMElement.getVBox().getChildren().addAll(gameTitleLabel,gameBtn); // add uiROMElement to games list games.add(uiROMElement); } //add all games saved in games(ArrayList) to gamesAnchorPane void addUIData() { for(int i=0; i> 16; int g = (hex & 0xFF00) >> 8; int b = (hex & 0xFF); return r + ", " + g + ", " + b; } /** * * @param originalImage original image which size is changed * @param type type of the original image (PNG,JPEG,...) * @param imgWidth wanted width * @param imgHeigth wanted height * @return the rezised image */ private static BufferedImage resizeImage(BufferedImage originalImage, int type, int imgWidth, int imgHeigth) { BufferedImage resizedImage = new BufferedImage(imgWidth, imgHeigth, type); Graphics2D g = resizedImage.createGraphics(); g.drawImage(originalImage, 0, 0, imgWidth, imgHeigth, null); g.dispose(); return resizedImage; } public String getCemuPath() { return cemuPath; } public void setCemuPath(String cemuPath) { this.cemuPath = cemuPath; } public String getRomPath() { return romPath; } public void setRomPath(String romPath) { this.romPath = romPath; } public String getColor() { return color; } public void setColor(String color) { this.color = color; } public int getxPos() { return xPos; } public void setxPos(int xPos) { this.xPos = xPos; } public int getyPos() { return yPos; } public void setyPos(int yPos) { this.yPos = yPos; } public int getxPosHelper() { return xPosHelper; } public void setxPosHelper(int xPosHelper) { this.xPosHelper = xPosHelper; } public boolean isFullscreen() { return fullscreen; } public void setFullscreen(boolean fullscreen) { this.fullscreen = fullscreen; } public boolean isCloudSync() { return cloudSync; } public void setCloudSync(boolean cloudSync) { this.cloudSync = cloudSync; } public boolean isAutoUpdate() { return autoUpdate; } public void setAutoUpdate(boolean autoUpdate) { this.autoUpdate = autoUpdate; } public boolean isUseBeta() { return useBeta; } public void setUseBeta(boolean useBeta) { this.useBeta = useBeta; } public String getGameExecutePath() { return gameExecutePath; } public void setGameExecutePath(String gameExecutePath) { this.gameExecutePath = gameExecutePath; } public String getSelectedGameTitleID() { return selectedGameTitleID; } public void setSelectedGameTitleID(String selectedGameTitleID) { this.selectedGameTitleID = selectedGameTitleID; } public String getCloudService() { return cloudService; } public void setCloudService(String cloudService) { this.cloudService = cloudService; } public JFXButton getPlayBtn() { return playBtn; } public void setPlayBtn(JFXButton playBtn) { this.playBtn = playBtn; } public double getWindowWidth() { return windowWidth; } public void setWindowWidth(double windowWidth) { this.windowWidth = windowWidth; } public double getWindowHeight() { return windowHeight; } public void setWindowHeight(double windowHeight) { this.windowHeight = windowHeight; } public int getOldXPosHelper() { return oldXPosHelper; } public void setOldXPosHelper(int oldXPosHelper) { this.oldXPosHelper = oldXPosHelper; } public AnchorPane getMainAnchorPane() { return mainAnchorPane; } public JFXButton getUpdateBtn() { return updateBtn; } }