/* Copyright 2019-2020 Dmitry Isaenko This file is part of NS-USBloader. NS-USBloader 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. NS-USBloader 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 NS-USBloader. If not, see . */ package nsusbloader.Controllers; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.fxml.FXML; import javafx.fxml.Initializable; import javafx.scene.control.*; import javafx.scene.input.DragEvent; import javafx.scene.input.TransferMode; import javafx.scene.layout.AnchorPane; import javafx.scene.layout.Region; import javafx.stage.DirectoryChooser; import javafx.stage.FileChooser; import nsusbloader.AppPreferences; import nsusbloader.com.net.NETCommunications; import nsusbloader.com.usb.UsbCommunications; import nsusbloader.FilesHelper; import nsusbloader.MediatorControl; import nsusbloader.ModelControllers.CancellableRunnable; import nsusbloader.NSLDataTypes.EModule; import nsusbloader.ServiceWindow; import java.io.File; import java.net.URL; import java.util.LinkedList; import java.util.List; import java.util.ResourceBundle; public class GamesController implements Initializable { @FXML private AnchorPane usbNetPane; @FXML private ChoiceBox choiceProtocol, choiceNetUsb; @FXML private Label nsIpLbl; @FXML private TextField nsIpTextField; @FXML private Button switchThemeBtn; @FXML public NSTableViewController tableFilesListController; // Accessible from Mediator (for drag-n-drop support) @FXML private Button selectNspBtn, selectSplitNspBtn, uploadStopBtn; private String previouslyOpenedPath; private Region btnUpStopImage; private ResourceBundle resourceBundle; private CancellableRunnable usbNetCommunications; private Thread workThread; @Override public void initialize(URL url, ResourceBundle resourceBundle) { this.resourceBundle = resourceBundle; ObservableList choiceProtocolList = FXCollections.observableArrayList("TinFoil", "GoldLeaf"); choiceProtocol.setItems(choiceProtocolList); choiceProtocol.getSelectionModel().select(AppPreferences.getInstance().getProtocol()); choiceProtocol.setOnAction(e-> { tableFilesListController.setNewProtocol(getSelectedProtocol()); if (getSelectedProtocol().equals("GoldLeaf")) { choiceNetUsb.setDisable(true); choiceNetUsb.getSelectionModel().select("USB"); nsIpLbl.setVisible(false); nsIpTextField.setVisible(false); } else { choiceNetUsb.setDisable(false); if (getSelectedNetUsb().equals("NET")) { nsIpLbl.setVisible(true); nsIpTextField.setVisible(true); } } // Really bad disable-enable upload button function disableUploadStopBtn(tableFilesListController.isFilesForUploadListEmpty()); }); // Add listener to notify tableView controller tableFilesListController.setNewProtocol(getSelectedProtocol()); // Notify tableView controller ObservableList choiceNetUsbList = FXCollections.observableArrayList("USB", "NET"); choiceNetUsb.setItems(choiceNetUsbList); choiceNetUsb.getSelectionModel().select(AppPreferences.getInstance().getNetUsb()); if (getSelectedProtocol().equals("GoldLeaf")) { choiceNetUsb.setDisable(true); choiceNetUsb.getSelectionModel().select("USB"); } choiceNetUsb.setOnAction(e->{ if (getSelectedNetUsb().equals("NET")){ nsIpLbl.setVisible(true); nsIpTextField.setVisible(true); } else{ nsIpLbl.setVisible(false); nsIpTextField.setVisible(false); } }); // Set and configure NS IP field behavior nsIpTextField.setText(AppPreferences.getInstance().getNsIp()); if (getSelectedProtocol().equals("TinFoil") && getSelectedNetUsb().equals("NET")){ nsIpLbl.setVisible(true); nsIpTextField.setVisible(true); } nsIpTextField.setTextFormatter(new TextFormatter<>(change -> { if (change.getControlNewText().contains(" ") | change.getControlNewText().contains("\t")) return null; else return change; })); // Set and configure switch theme button Region btnSwitchImage = new Region(); btnSwitchImage.getStyleClass().add("regionLamp"); switchThemeBtn.setGraphic(btnSwitchImage); this.switchThemeBtn.setOnAction(e->switchTheme()); uploadStopBtn.setDisable(getSelectedProtocol().equals("TinFoil")); selectNspBtn.setOnAction(e-> selectFilesBtnAction()); selectSplitNspBtn.setOnAction(e-> selectSplitBtnAction()); selectSplitNspBtn.getStyleClass().add("buttonSelect"); uploadStopBtn.setOnAction(e-> uploadBtnAction()); selectNspBtn.getStyleClass().add("buttonSelect"); this.btnUpStopImage = new Region(); btnUpStopImage.getStyleClass().add("regionUpload"); uploadStopBtn.getStyleClass().add("buttonUp"); uploadStopBtn.setGraphic(btnUpStopImage); this.previouslyOpenedPath = AppPreferences.getInstance().getRecent(); } /** * Changes UI theme on the go * */ private void switchTheme(){ final String darkTheme = "/res/app_dark.css"; final String lightTheme = "/res/app_light.css"; final ObservableList styleSheets = switchThemeBtn.getScene().getStylesheets(); if (styleSheets.get(0).equals(darkTheme)) { styleSheets.remove(darkTheme); styleSheets.add(lightTheme); } else { styleSheets.remove(lightTheme); styleSheets.add(darkTheme); } AppPreferences.getInstance().setTheme(styleSheets.get(0)); } /** * Get selected protocol (GL/TF) * */ String getSelectedProtocol(){ return choiceProtocol.getSelectionModel().getSelectedItem(); } /** * Get selected protocol (USB/NET) * */ String getSelectedNetUsb(){ return choiceNetUsb.getSelectionModel().getSelectedItem(); } /** * Get NS IP address * */ String getNsIp(){ return nsIpTextField.getText(); } /** * Functionality for selecting NSP button. * */ private void selectFilesBtnAction(){ List filesList; FileChooser fileChooser = new FileChooser(); fileChooser.setTitle(resourceBundle.getString("btn_OpenFile")); fileChooser.setInitialDirectory(new File(FilesHelper.getRealFolder(previouslyOpenedPath))); if (getSelectedProtocol().equals("TinFoil") && MediatorControl.getInstance().getContoller().getSettingsCtrlr().getTinfoilSettings().isXciNszXczSupport()) fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("NSP/XCI/NSZ/XCZ", "*.nsp", "*.xci", "*.nsz", "*.xcz")); else if (getSelectedProtocol().equals("GoldLeaf") && (! MediatorControl.getInstance().getContoller().getSettingsCtrlr().getGoldleafSettings().getNSPFileFilterForGL())) fileChooser.getExtensionFilters().addAll(new FileChooser.ExtensionFilter("Any file", "*.*"), new FileChooser.ExtensionFilter("NSP ROM", "*.nsp") ); else fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("NSP ROM", "*.nsp")); filesList = fileChooser.showOpenMultipleDialog(usbNetPane.getScene().getWindow()); if (filesList != null && !filesList.isEmpty()) { tableFilesListController.setFiles(filesList); uploadStopBtn.setDisable(false); previouslyOpenedPath = filesList.get(0).getParent(); } } /** * Functionality for selecting Split NSP button. * */ private void selectSplitBtnAction(){ File splitFile; DirectoryChooser dirChooser = new DirectoryChooser(); dirChooser.setTitle(resourceBundle.getString("btn_OpenFile")); String saveToLocation = FilesHelper.getRealFolder(previouslyOpenedPath); dirChooser.setInitialDirectory(new File(saveToLocation)); splitFile = dirChooser.showDialog(usbNetPane.getScene().getWindow()); if (splitFile != null && splitFile.getName().toLowerCase().endsWith(".nsp")) { tableFilesListController.setFile(splitFile); uploadStopBtn.setDisable(false); // Is it useful? previouslyOpenedPath = splitFile.getParent(); } } /** * It's button listener when no transmission executes * */ private void uploadBtnAction(){ if (workThread != null && workThread.isAlive()) return; // Collect files List nspToUpload; TextArea logArea = MediatorControl.getInstance().getContoller().logArea; if (getSelectedProtocol().equals("TinFoil") && tableFilesListController.getFilesForUpload() == null) { logArea.setText(resourceBundle.getString("tab3_Txt_NoFolderOrFileSelected")); return; } if ((nspToUpload = tableFilesListController.getFilesForUpload()) != null){ logArea.setText(resourceBundle.getString("tab3_Txt_FilesToUploadTitle")+"\n"); nspToUpload.forEach(item -> logArea.appendText(" "+item.getAbsolutePath()+"\n")); } else { logArea.clear(); nspToUpload = new LinkedList<>(); } SettingsController settings = MediatorControl.getInstance().getContoller().getSettingsCtrlr(); // If USB selected if (getSelectedProtocol().equals("GoldLeaf") ){ final SettingsBlockGoldleafController goldleafSettings = settings.getGoldleafSettings(); usbNetCommunications = new UsbCommunications(nspToUpload, "GoldLeaf" + goldleafSettings.getGlVer(), goldleafSettings.getNSPFileFilterForGL()); } else if (( getSelectedProtocol().equals("TinFoil") && getSelectedNetUsb().equals("USB") )){ usbNetCommunications = new UsbCommunications(nspToUpload, "TinFoil", false); } else { // NET INSTALL OVER TINFOIL final String ipValidationPattern = "^([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])$"; final SettingsBlockTinfoilController tinfoilSettings = settings.getTinfoilSettings(); if (tinfoilSettings.isValidateNSHostName() && ! getNsIp().matches(ipValidationPattern)) { if (!ServiceWindow.getConfirmationWindow(resourceBundle.getString("windowTitleBadIp"), resourceBundle.getString("windowBodyBadIp"))) return; } String nsIP = getNsIp(); if (! tinfoilSettings.isExpertModeSelected()) usbNetCommunications = new NETCommunications(nspToUpload, nsIP, false, "", "", ""); else { usbNetCommunications = new NETCommunications( nspToUpload, nsIP, tinfoilSettings.isNoRequestsServe(), tinfoilSettings.isAutoDetectIp()?"":tinfoilSettings.getHostIp(), tinfoilSettings.isRandomlySelectPort()?"":tinfoilSettings.getHostPort(), tinfoilSettings.isNoRequestsServe()?tinfoilSettings.getHostExtra():"" ); } } workThread = new Thread(usbNetCommunications); workThread.setDaemon(true); workThread.start(); } /** * It's button listener when transmission in progress * */ private void stopBtnAction(){ if (workThread == null || ! workThread.isAlive()) return; usbNetCommunications.cancel(); if (usbNetCommunications instanceof NETCommunications){ try{ ((NETCommunications) usbNetCommunications).getServerSocket().close(); ((NETCommunications) usbNetCommunications).getClientSocket().close(); } catch (Exception ignore){ } } } /** * Drag-n-drop support (dragOver consumer) * */ @FXML private void handleDragOver(DragEvent event){ if (event.getDragboard().hasFiles() && ! MediatorControl.getInstance().getTransferActive()) event.acceptTransferModes(TransferMode.ANY); event.consume(); } /** * Drag-n-drop support (drop consumer) * */ @FXML private void handleDrop(DragEvent event){ List filesDropped = event.getDragboard().getFiles(); SettingsController settingsController = MediatorControl.getInstance().getContoller().getSettingsCtrlr(); SettingsBlockTinfoilController tinfoilSettings = settingsController.getTinfoilSettings(); SettingsBlockGoldleafController goldleafController = settingsController.getGoldleafSettings(); if (getSelectedProtocol().equals("TinFoil") && tinfoilSettings.isXciNszXczSupport()) filesDropped.removeIf(file -> ! file.getName().toLowerCase().matches("(.*\\.nsp$)|(.*\\.xci$)|(.*\\.nsz$)|(.*\\.xcz$)")); else if (getSelectedProtocol().equals("GoldLeaf") && (! goldleafController.getNSPFileFilterForGL())) filesDropped.removeIf(file -> (file.isDirectory() && ! file.getName().toLowerCase().matches(".*\\.nsp$"))); else filesDropped.removeIf(file -> ! file.getName().toLowerCase().matches(".*\\.nsp$")); if ( ! filesDropped.isEmpty() ) tableFilesListController.setFiles(filesDropped); event.setDropCompleted(true); event.consume(); } /** * This thing modify UI for reusing 'Upload to NS' button and make functionality set for "Stop transmission" * Called from mediator * TODO: remove shitcoding practices * */ public void notifyThreadStarted(boolean isActive, EModule type){ if (! type.equals(EModule.USB_NET_TRANSFERS)){ usbNetPane.setDisable(isActive); return; } selectNspBtn.setDisable(isActive); selectSplitNspBtn.setDisable(isActive); btnUpStopImage.getStyleClass().clear(); if (isActive) { btnUpStopImage.getStyleClass().add("regionStop"); uploadStopBtn.setOnAction(e-> stopBtnAction()); uploadStopBtn.setText(resourceBundle.getString("btn_Stop")); uploadStopBtn.getStyleClass().remove("buttonUp"); uploadStopBtn.getStyleClass().add("buttonStop"); } else { btnUpStopImage.getStyleClass().add("regionUpload"); uploadStopBtn.setOnAction(e-> uploadBtnAction()); uploadStopBtn.setText(resourceBundle.getString("btn_Upload")); uploadStopBtn.getStyleClass().remove("buttonStop"); uploadStopBtn.getStyleClass().add("buttonUp"); } } /** * Crunch. This function called from NSTableViewController * */ public void disableUploadStopBtn(boolean disable){ if (getSelectedProtocol().equals("TinFoil")) uploadStopBtn.setDisable(disable); else uploadStopBtn.setDisable(false); } /** * Get 'Recent' path */ public String getRecentPath(){ return previouslyOpenedPath; } public void updatePreferencesOnExit(){ AppPreferences preferences = AppPreferences.getInstance(); preferences.setProtocol(getSelectedProtocol()); preferences.setRecent(getRecentPath()); preferences.setNetUsb(getSelectedNetUsb()); preferences.setNsIp(getNsIp()); } }