diff --git a/src/main/java/nsusbloader/COM/NET/NETCommunications.java b/src/main/java/nsusbloader/COM/NET/NETCommunications.java index da241aa..f91cd6f 100644 --- a/src/main/java/nsusbloader/COM/NET/NETCommunications.java +++ b/src/main/java/nsusbloader/COM/NET/NETCommunications.java @@ -3,6 +3,7 @@ package nsusbloader.COM.NET; import javafx.concurrent.Task; import nsusbloader.NSLDataTypes.EFileStatus; import nsusbloader.ModelControllers.LogPrinter; +import nsusbloader.NSLDataTypes.EModule; import nsusbloader.NSLDataTypes.EMsgType; import nsusbloader.COM.Helpers.NSSplitReader; @@ -42,7 +43,7 @@ public class NETCommunications extends Task<Void> { // todo: thows IOException? else this.extras = ""; this.switchIP = switchIP; - this.logPrinter = new LogPrinter(); + this.logPrinter = new LogPrinter(EModule.USB_NET_TRANSFERS); this.nspMap = new HashMap<>(); this.nspFileSizes = new HashMap<>(); // Filter and remove empty/incorrect split-files diff --git a/src/main/java/nsusbloader/COM/USB/UsbCommunications.java b/src/main/java/nsusbloader/COM/USB/UsbCommunications.java index 44dc65a..014eb14 100644 --- a/src/main/java/nsusbloader/COM/USB/UsbCommunications.java +++ b/src/main/java/nsusbloader/COM/USB/UsbCommunications.java @@ -3,6 +3,7 @@ package nsusbloader.COM.USB; import javafx.concurrent.Task; import nsusbloader.ModelControllers.LogPrinter; import nsusbloader.NSLDataTypes.EFileStatus; +import nsusbloader.NSLDataTypes.EModule; import nsusbloader.NSLDataTypes.EMsgType; import org.usb4java.*; @@ -34,7 +35,7 @@ public class UsbCommunications extends Task<Void> { this.nspMap = new LinkedHashMap<>(); for (File f: nspList) nspMap.put(f.getName(), f); - this.logPrinter = new LogPrinter(); + this.logPrinter = new LogPrinter(EModule.USB_NET_TRANSFERS); } @Override diff --git a/src/main/java/nsusbloader/Controllers/FrontController.java b/src/main/java/nsusbloader/Controllers/FrontController.java index a7d71c8..9f99177 100644 --- a/src/main/java/nsusbloader/Controllers/FrontController.java +++ b/src/main/java/nsusbloader/Controllers/FrontController.java @@ -6,6 +6,7 @@ import javafx.concurrent.Task; import javafx.fxml.FXML; import javafx.fxml.Initializable; import javafx.scene.control.*; +import javafx.scene.layout.AnchorPane; import javafx.scene.layout.Pane; import javafx.scene.layout.Region; import javafx.stage.DirectoryChooser; @@ -14,6 +15,7 @@ import nsusbloader.AppPreferences; import nsusbloader.COM.NET.NETCommunications; import nsusbloader.COM.USB.UsbCommunications; import nsusbloader.MediatorControl; +import nsusbloader.NSLDataTypes.EModule; import nsusbloader.ServiceWindow; import java.io.File; @@ -25,6 +27,8 @@ import java.util.ResourceBundle; public class FrontController implements Initializable { @FXML private Pane specialPane; + @FXML + private AnchorPane usbNetPane; @FXML private ChoiceBox<String> choiceProtocol, choiceNetUsb; @@ -35,7 +39,7 @@ public class FrontController implements Initializable { @FXML private Button switchThemeBtn; @FXML - public NSTableViewController tableFilesListController; // Accessible from Mediator + public NSTableViewController tableFilesListController; // Accessible from Mediator (for drag-n-drop support) @FXML private Button selectNspBtn, selectSplitNspBtn, uploadStopBtn; @@ -167,7 +171,7 @@ public class FrontController implements Initializable { } - /********************************************************************************************************************/ + /*-****************************************************************************************************************-*/ /** * Functionality for selecting NSP button. * */ @@ -290,30 +294,30 @@ public class FrontController implements Initializable { * Called from mediator * TODO: remove shitcoding practices * */ - public void notifyTransmissionStarted(boolean isTransmissionStarted){ - if (isTransmissionStarted) { + public void notifyTransmThreadStarted(boolean isActive, EModule type){ + if (! type.equals(EModule.USB_NET_TRANSFERS)){ + usbNetPane.setDisable(isActive); + return; + } + if (isActive) { selectNspBtn.setDisable(true); selectSplitNspBtn.setDisable(true); - uploadStopBtn.setOnAction(e-> stopBtnAction()); - - uploadStopBtn.setText(resourceBundle.getString("btn_Stop")); - - btnUpStopImage.getStyleClass().remove("regionUpload"); + btnUpStopImage.getStyleClass().clear(); btnUpStopImage.getStyleClass().add("regionStop"); + uploadStopBtn.setOnAction(e-> stopBtnAction()); + uploadStopBtn.setText(resourceBundle.getString("btn_Stop")); uploadStopBtn.getStyleClass().remove("buttonUp"); uploadStopBtn.getStyleClass().add("buttonStop"); return; } selectNspBtn.setDisable(false); selectSplitNspBtn.setDisable(false); - uploadStopBtn.setOnAction(e-> uploadBtnAction()); - - uploadStopBtn.setText(resourceBundle.getString("btn_Upload")); - - btnUpStopImage.getStyleClass().remove("regionStop"); + btnUpStopImage.getStyleClass().clear(); btnUpStopImage.getStyleClass().add("regionUpload"); + uploadStopBtn.setOnAction(e-> uploadBtnAction()); + uploadStopBtn.setText(resourceBundle.getString("btn_Upload")); uploadStopBtn.getStyleClass().remove("buttonStop"); uploadStopBtn.getStyleClass().add("buttonUp"); } diff --git a/src/main/java/nsusbloader/Controllers/NSLMainController.java b/src/main/java/nsusbloader/Controllers/NSLMainController.java index 478bd42..c1f58a4 100644 --- a/src/main/java/nsusbloader/Controllers/NSLMainController.java +++ b/src/main/java/nsusbloader/Controllers/NSLMainController.java @@ -119,6 +119,10 @@ public class NSLMainController implements Initializable { public FrontController getFrontCtrlr(){ return FrontTabController; } + + public SplitMergeController getSmCtrlr(){ + return SplitMergeTabController; + } /** * Save preferences before exit * */ diff --git a/src/main/java/nsusbloader/Controllers/SplitMergeController.java b/src/main/java/nsusbloader/Controllers/SplitMergeController.java index 4d9cae5..de327c5 100644 --- a/src/main/java/nsusbloader/Controllers/SplitMergeController.java +++ b/src/main/java/nsusbloader/Controllers/SplitMergeController.java @@ -3,15 +3,14 @@ package nsusbloader.Controllers; import javafx.concurrent.Task; import javafx.fxml.FXML; import javafx.fxml.Initializable; -import javafx.scene.control.Button; -import javafx.scene.control.Label; -import javafx.scene.control.RadioButton; -import javafx.scene.control.ToggleGroup; +import javafx.scene.control.*; import javafx.scene.layout.Region; +import javafx.scene.layout.VBox; import javafx.stage.DirectoryChooser; import javafx.stage.FileChooser; import nsusbloader.AppPreferences; import nsusbloader.MediatorControl; +import nsusbloader.NSLDataTypes.EModule; import nsusbloader.ServiceWindow; import nsusbloader.Utilities.SplitMergeTool; @@ -22,6 +21,9 @@ import java.util.ResourceBundle; public class SplitMergeController implements Initializable { @FXML private ToggleGroup splitMergeTogGrp; + @FXML + private VBox smToolPane; + @FXML private RadioButton splitRad, mergeRad; @FXML @@ -31,16 +33,23 @@ public class SplitMergeController implements Initializable { @FXML private Label fileFolderLabelLbl, fileFolderActualPathLbl, - saveToPathLbl; + saveToPathLbl, + statusLbl; + + private ResourceBundle resourceBundle; private Region convertRegion; + private Task<Boolean> smTask; + private Thread smThread; @Override public void initialize(URL url, ResourceBundle resourceBundle) { + this.resourceBundle = resourceBundle; convertRegion = new Region(); convertBtn.setGraphic(convertRegion); splitRad.setOnAction((actionEvent -> { + statusLbl.setText(""); convertRegion.getStyleClass().clear(); convertRegion.getStyleClass().add("regionSplitToOne"); fileFolderLabelLbl.setText(resourceBundle.getString("tabSplMrg_Txt_File")); @@ -49,6 +58,7 @@ public class SplitMergeController implements Initializable { convertBtn.setDisable(true); })); mergeRad.setOnAction((actionEvent -> { + statusLbl.setText(""); convertRegion.getStyleClass().clear(); convertRegion.getStyleClass().add("regionOneToSplit"); fileFolderLabelLbl.setText(resourceBundle.getString("tabSplMrg_Txt_Folder")); @@ -74,6 +84,7 @@ public class SplitMergeController implements Initializable { })); selectFileFolderBtn.setOnAction(actionEvent -> { + statusLbl.setText(""); if (splitRad.isSelected()) { FileChooser fc = new FileChooser(); fc.setTitle(resourceBundle.getString("tabSplMrg_Btn_SelectFile")); @@ -102,51 +113,75 @@ public class SplitMergeController implements Initializable { } }); - convertBtn.setOnAction(actionEvent -> { - if (MediatorControl.getInstance().getTransferActive()) { - ServiceWindow.getErrorNotification(resourceBundle.getString("windowTitleError"), resourceBundle.getString("windowBodyPleaseFinishTransfersFirst")); - return; - } - - if (splitRad.isSelected()){ - updateProcess(true); - Task<Void> task = SplitMergeTool.splitFile(fileFolderActualPathLbl.getText(), saveToPathLbl.getText()); - task.setOnSucceeded(workerStateEvent -> this.updateProcess(false)); - Thread thread = new Thread(task); - thread.setDaemon(true); - thread.start(); - } - else{ - updateProcess(true); - Task<Void> task = SplitMergeTool.mergeFile(fileFolderActualPathLbl.getText(), saveToPathLbl.getText()); - task.setOnSucceeded(workerStateEvent -> this.updateProcess(false)); - Thread thread = new Thread(task); - thread.setDaemon(true); - thread.start(); - } - - }); + convertBtn.setOnAction(actionEvent -> setConvertBtnAction()); } - private void updateProcess(boolean isStart){ + public void notifySmThreadStarted(boolean isStart, EModule type){ + if (! type.equals(EModule.SPLIT_MERGE_TOOL)){ + smToolPane.setDisable(isStart); + return; + } if (isStart){ MediatorControl.getInstance().getContoller().logArea.clear(); - MediatorControl.getInstance().setTransferActive(true); // TODO: remove & rewrite to interrupt function - convertBtn.setDisable(true);// TODO: remove & rewrite to interrupt function splitRad.setDisable(true); mergeRad.setDisable(true); selectFileFolderBtn.setDisable(true); changeSaveToBtn.setDisable(true); + + convertBtn.setOnAction(e -> stopBtnAction()); + convertBtn.setText(resourceBundle.getString("btn_Stop")); + convertRegion.getStyleClass().clear(); + convertRegion.getStyleClass().add("regionStop"); return; } - MediatorControl.getInstance().setTransferActive(false); - convertBtn.setDisable(false);// TODO: remove & rewrite to interrupt function splitRad.setDisable(false); mergeRad.setDisable(false); selectFileFolderBtn.setDisable(false); changeSaveToBtn.setDisable(false); + + convertBtn.setOnAction(e -> setConvertBtnAction()); + convertBtn.setText(resourceBundle.getString("tabSplMrg_Btn_Convert")); + convertRegion.getStyleClass().clear(); + if (splitRad.isSelected()) + convertRegion.getStyleClass().add("regionSplitToOne"); + else + convertRegion.getStyleClass().add("regionOneToSplit"); } + /** + * It's button listener when convert-process in progress + * */ + private void stopBtnAction(){ + if (smThread != null && smThread.isAlive()) + smTask.cancel(false); + } + /** + * It's button listener when convert-process NOT in progress + * */ + private void setConvertBtnAction(){ + if (MediatorControl.getInstance().getTransferActive()) { + ServiceWindow.getErrorNotification(resourceBundle.getString("windowTitleError"), resourceBundle.getString("windowBodyPleaseFinishTransfersFirst")); + return; + } + + if (splitRad.isSelected()) + smTask = SplitMergeTool.splitFile(fileFolderActualPathLbl.getText(), saveToPathLbl.getText()); + else + smTask = SplitMergeTool.mergeFile(fileFolderActualPathLbl.getText(), saveToPathLbl.getText()); + smTask.setOnCancelled(event -> statusLbl.setText(resourceBundle.getString("failure_txt"))); + smTask.setOnSucceeded(event -> { + if (smTask.getValue()) + statusLbl.setText(resourceBundle.getString("done_txt")); + else + statusLbl.setText(resourceBundle.getString("failure_txt")); + }); + smThread = new Thread(smTask); + smThread.setDaemon(true); + smThread.start(); + } + /** + * Save application settings on exit + * */ public void updatePreferencesOnExit(){ if (splitRad.isSelected()) AppPreferences.getInstance().setSplitMergeType(0); diff --git a/src/main/java/nsusbloader/MediatorControl.java b/src/main/java/nsusbloader/MediatorControl.java index 2db197e..12d9c38 100644 --- a/src/main/java/nsusbloader/MediatorControl.java +++ b/src/main/java/nsusbloader/MediatorControl.java @@ -1,12 +1,13 @@ package nsusbloader; import nsusbloader.Controllers.NSLMainController; +import nsusbloader.NSLDataTypes.EModule; import java.util.concurrent.atomic.AtomicBoolean; public class MediatorControl { private AtomicBoolean isTransferActive = new AtomicBoolean(false); // Overcoded just for sure - private NSLMainController applicationController; + private NSLMainController mainCtrler; public static MediatorControl getInstance(){ return MediatorControlHold.INSTANCE; @@ -16,13 +17,14 @@ public class MediatorControl { private static final MediatorControl INSTANCE = new MediatorControl(); } public void setController(NSLMainController controller){ - this.applicationController = controller; + this.mainCtrler = controller; } - public NSLMainController getContoller(){ return this.applicationController; } + public NSLMainController getContoller(){ return this.mainCtrler; } - public synchronized void setTransferActive(boolean state) { - isTransferActive.set(state); - applicationController.getFrontCtrlr().notifyTransmissionStarted(state); + public synchronized void setBgThreadActive(boolean isActive, EModule appModuleType) { + isTransferActive.set(isActive); + mainCtrler.getFrontCtrlr().notifyTransmThreadStarted(isActive, appModuleType); + mainCtrler.getSmCtrlr().notifySmThreadStarted(isActive, appModuleType); } public synchronized boolean getTransferActive() { return this.isTransferActive.get(); } } diff --git a/src/main/java/nsusbloader/ModelControllers/LogPrinter.java b/src/main/java/nsusbloader/ModelControllers/LogPrinter.java index 4d0c34d..67cb326 100644 --- a/src/main/java/nsusbloader/ModelControllers/LogPrinter.java +++ b/src/main/java/nsusbloader/ModelControllers/LogPrinter.java @@ -1,6 +1,7 @@ package nsusbloader.ModelControllers; import nsusbloader.NSLDataTypes.EFileStatus; +import nsusbloader.NSLDataTypes.EModule; import nsusbloader.NSLDataTypes.EMsgType; import java.io.File; @@ -14,11 +15,11 @@ public class LogPrinter { private BlockingQueue<Double> progressQueue; private HashMap<String, EFileStatus> statusMap; // BlockingQueue for literally one object. TODO: read more books ; replace to hashMap - public LogPrinter(){ + public LogPrinter(EModule whoIsAsking){ this.msgQueue = new LinkedBlockingQueue<>(); this.progressQueue = new LinkedBlockingQueue<>(); this.statusMap = new HashMap<>(); - this.msgConsumer = new MessagesConsumer(this.msgQueue, this.progressQueue, this.statusMap); + this.msgConsumer = new MessagesConsumer(whoIsAsking, this.msgQueue, this.progressQueue, this.statusMap); this.msgConsumer.start(); } /** @@ -42,7 +43,8 @@ public class LogPrinter { default: msgQueue.put(message); } - }catch (InterruptedException ie){ + } + catch (InterruptedException ie){ ie.printStackTrace(); } } diff --git a/src/main/java/nsusbloader/ModelControllers/MessagesConsumer.java b/src/main/java/nsusbloader/ModelControllers/MessagesConsumer.java index e3fa778..a7384f4 100644 --- a/src/main/java/nsusbloader/ModelControllers/MessagesConsumer.java +++ b/src/main/java/nsusbloader/ModelControllers/MessagesConsumer.java @@ -7,6 +7,7 @@ import javafx.scene.control.TextArea; import nsusbloader.Controllers.NSTableViewController; import nsusbloader.MediatorControl; import nsusbloader.NSLDataTypes.EFileStatus; +import nsusbloader.NSLDataTypes.EModule; import java.util.ArrayList; import java.util.HashMap; @@ -20,10 +21,12 @@ public class MessagesConsumer extends AnimationTimer { private final ProgressBar progressBar; private final HashMap<String, EFileStatus> statusMap; private final NSTableViewController tableViewController; + private final EModule appModuleType; private boolean isInterrupted; - MessagesConsumer(BlockingQueue<String> msgQueue, BlockingQueue<Double> progressQueue, HashMap<String, EFileStatus> statusMap){ + MessagesConsumer(EModule appModuleType, BlockingQueue<String> msgQueue, BlockingQueue<Double> progressQueue, HashMap<String, EFileStatus> statusMap){ + this.appModuleType = appModuleType; this.isInterrupted = false; this.msgQueue = msgQueue; @@ -38,7 +41,7 @@ public class MessagesConsumer extends AnimationTimer { progressBar.setProgress(0.0); progressBar.setProgress(ProgressIndicator.INDETERMINATE_PROGRESS); - MediatorControl.getInstance().setTransferActive(true); + MediatorControl.getInstance().setBgThreadActive(true, appModuleType); } @Override @@ -60,7 +63,7 @@ public class MessagesConsumer extends AnimationTimer { } if (isInterrupted) { // It's safe 'cuz it's could't be interrupted while HashMap populating - MediatorControl.getInstance().setTransferActive(false); + MediatorControl.getInstance().setBgThreadActive(false, appModuleType); progressBar.setProgress(0.0); if (statusMap.size() > 0) diff --git a/src/main/java/nsusbloader/NSLDataTypes/EModule.java b/src/main/java/nsusbloader/NSLDataTypes/EModule.java new file mode 100644 index 0000000..48209d1 --- /dev/null +++ b/src/main/java/nsusbloader/NSLDataTypes/EModule.java @@ -0,0 +1,7 @@ +package nsusbloader.NSLDataTypes; + +public enum EModule { + USB_NET_TRANSFERS, + SPLIT_MERGE_TOOL, + RCM +} diff --git a/src/main/java/nsusbloader/Utilities/SplitMergeTool.java b/src/main/java/nsusbloader/Utilities/SplitMergeTool.java index 3682ed8..9481fad 100644 --- a/src/main/java/nsusbloader/Utilities/SplitMergeTool.java +++ b/src/main/java/nsusbloader/Utilities/SplitMergeTool.java @@ -2,205 +2,284 @@ package nsusbloader.Utilities; import javafx.concurrent.Task; import nsusbloader.ModelControllers.LogPrinter; +import nsusbloader.NSLDataTypes.EModule; import nsusbloader.NSLDataTypes.EMsgType; import java.io.*; import java.util.Arrays; public class SplitMergeTool { - // TODO: ADD ABILITY TO INTERRUPT PROCESS - public static Task<Void> splitFile(String filePath, String saveToPath){ - LogPrinter logPrinter = new LogPrinter(); - return new Task<Void>() { - @Override - protected Void call() { - File file = new File(filePath); - File folder = new File(saveToPath+File.separator+"!_"+file.getName()); - - logPrinter.print("Split file: "+filePath, EMsgType.INFO); - - for (int i = 0; i < 50; i++){ - if (! folder.mkdir()){ - if (folder.exists()){ - logPrinter.print("Trying to create a good new folder...", EMsgType.WARNING); - folder = new File(saveToPath+File.separator+"!_"+i+"_"+file.getName()); - continue; - } - else { // folder not created and not exists - return - logPrinter.print("Folder "+folder.getAbsolutePath()+" could not be created. Not enough rights or something like that?", EMsgType.FAIL); - logPrinter.close(); - return null; - } - } - logPrinter.print("Save results to: "+folder.getAbsolutePath(), EMsgType.INFO); - break; - } - - try{ - BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file)); - - BufferedOutputStream fragmentBos; - - long counter; - - long originalFileLen = file.length(); - - double chunkPercent = (4194240.0 / (originalFileLen / 100.0) / 100.0); - long totalSizeCnt = 0; - - byte[] chunk; - int readBytesCnt; - - main_loop: - for (int i = 0; ; i++){ - fragmentBos = new BufferedOutputStream( - new FileOutputStream(new File(folder.getAbsolutePath()+File.separator+String.format("%02d", i))) - ); - - counter = 0; - - while (counter < 1024){ // 0xffff0000 total - chunk = new byte[4194240]; - - if ((readBytesCnt = bis.read(chunk)) < 4194240){ - if (readBytesCnt > 0) - fragmentBos.write(chunk, 0, readBytesCnt); - fragmentBos.close(); - logPrinter.updateProgress(1.0); - break main_loop; - } - - fragmentBos.write(chunk); - - logPrinter.updateProgress(chunkPercent * totalSizeCnt); - counter++; // NOTE: here we have some redundancy of variables. It has to be fixed one day. - totalSizeCnt++; - } - fragmentBos.close(); - } - - bis.close(); - - //=============== let's check what we have ============== - logPrinter.print("Original file size: "+originalFileLen, EMsgType.INFO); - long totalChunksSize = 0; - File[] chunkFileArr = folder.listFiles(); - - if (chunkFileArr == null) { - logPrinter.print("Unable to check results. It means that something went wrong.", EMsgType.FAIL); - return null; - } - else { - Arrays.sort(chunkFileArr); - for (File chunkFile : chunkFileArr) { - logPrinter.print("Chunk " + chunkFile.getName() + " size: " + chunkFile.length(), EMsgType.INFO); - totalChunksSize += chunkFile.length(); - } - - logPrinter.print("Total chunks size: " + totalChunksSize, EMsgType.INFO); - - if (originalFileLen != totalChunksSize) - logPrinter.print("Sizes are different! Do NOT use this file for installations!", EMsgType.FAIL); - else - logPrinter.print("Sizes are the same! Split file should be good!", EMsgType.PASS); - } - } - catch (Exception e){ - e.printStackTrace(); - logPrinter.print("Error: "+e.getMessage(), EMsgType.FAIL); - } - logPrinter.print("Split task complete!", EMsgType.INFO); - logPrinter.close(); - - return null; - } - }; + public static Task<Boolean> splitFile(String filePath, String saveToPath){ + return new SplitTask(filePath, saveToPath); }; - // TODO: CHECK IF FILE WE'RE ABOUT TO CREATE IS EXISTS !!! - // TODO: not here: Add RECENT on current session level selection of the 'Select file/select folder' ? Already done ? - - public static Task<Void> mergeFile(String filePath, String saveToPath){ - LogPrinter logPrinter = new LogPrinter(); - - return new Task<Void>() { - @Override - protected Void call() { - logPrinter.print("Merge file: "+filePath, EMsgType.INFO); - - File folder = new File(filePath); - - long cnkTotalSize = 0; - - File[] chunkFiles = folder.listFiles((file, s) -> s.matches("^[0-9][0-9]$")); - - if (chunkFiles == null){ - logPrinter.print("Selected folder doesn't have any chunks. Nothing to do here.", EMsgType.FAIL); - logPrinter.close(); - return null; - } - - Arrays.sort(chunkFiles); - - logPrinter.print("Next files will be merged in following order: ", EMsgType.INFO); - for (File cnk : chunkFiles){ - logPrinter.print(" "+cnk.getName(), EMsgType.INFO); - cnkTotalSize += cnk.length(); - } - - double chunkPercent = (4194240.0 / (cnkTotalSize / 100.0) / 100.0); - long totalSizeCnt = 0; - - File resultFile = new File(saveToPath+File.separator+"!_"+folder.getName()); - - logPrinter.print("Save results to: "+resultFile.getAbsolutePath(), EMsgType.INFO); - try { - BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(resultFile)); - - BufferedInputStream bis; - byte[] chunk; - int readBytesCnt; - - for (File cnk : chunkFiles){ - bis = new BufferedInputStream(new FileInputStream(cnk)); - while (true){ - chunk = new byte[4194240]; - readBytesCnt = bis.read(chunk); - - logPrinter.updateProgress(chunkPercent * totalSizeCnt); - totalSizeCnt++; - - if (readBytesCnt < 4194240){ - if (readBytesCnt > 0) - bos.write(chunk, 0, readBytesCnt); - break; - } - - bos.write(chunk); - } - bis.close(); - } - bos.close(); - //=============== let's check what we have ============== - long resultFileSize = resultFile.length(); - logPrinter.print("Total chunks size: " + cnkTotalSize, EMsgType.INFO); - logPrinter.print("Merged file size: " + resultFileSize, EMsgType.INFO); - - if (cnkTotalSize != resultFileSize) - logPrinter.print("Sizes are different! Do NOT use this file for installations!", EMsgType.FAIL); - else - logPrinter.print("Sizes are the same! Split file should be good!", EMsgType.PASS); - } - catch (Exception e){ - e.printStackTrace(); - logPrinter.print("Error: "+e.getMessage(), EMsgType.FAIL); - } - - logPrinter.print("Merge task complete!", EMsgType.INFO); - logPrinter.close(); - return null; - } - }; + public static Task<Boolean> mergeFile(String filePath, String saveToPath){ + return new MergeTask(filePath, saveToPath); } } + +class SplitTask extends Task<Boolean>{ + + private LogPrinter logPrinter; + private String saveToPath; + private String filePath; + + SplitTask(String filePath, String saveToPath){ + this.filePath = filePath; + this.saveToPath = saveToPath; + logPrinter = new LogPrinter(EModule.SPLIT_MERGE_TOOL); + } + + @Override + protected Boolean call() { + File file = new File(filePath); + File folder = new File(saveToPath+File.separator+"!_"+file.getName()); + + logPrinter.print("Split file: "+filePath, EMsgType.INFO); + + for (int i = 0; ; i++){ + if (this.isCancelled()){ + logPrinter.print("Split task interrupted!", EMsgType.PASS); + logPrinter.close(); + return false; + } + if (! folder.mkdir()){ + if (folder.exists()){ + if (i >= 50){ + logPrinter.print("Can't create new file.", EMsgType.FAIL); + logPrinter.close(); + return false; + } + logPrinter.print("Trying to create a good new folder...", EMsgType.WARNING); + folder = new File(saveToPath+File.separator+"!_"+i+"_"+file.getName()); + continue; + } + else { // folder not created and not exists - return + logPrinter.print("Folder "+folder.getAbsolutePath()+" could not be created. Not enough rights or something like that?", EMsgType.FAIL); + logPrinter.close(); + return false; + } + } + logPrinter.print("Save results to: "+folder.getAbsolutePath(), EMsgType.INFO); + break; + } + + try{ + BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file)); + + BufferedOutputStream fragmentBos; + + long counter; + + long originalFileLen = file.length(); + + double chunkPercent = (4194240.0 / (originalFileLen / 100.0) / 100.0); + long totalSizeCnt = 0; + + byte[] chunk; + int readBytesCnt; + + main_loop: + for (int i = 0; ; i++){ + fragmentBos = new BufferedOutputStream( + new FileOutputStream(new File(folder.getAbsolutePath()+File.separator+String.format("%02d", i))) + ); + + counter = 0; + + while (counter < 1024){ // 0xffff0000 total + + if (this.isCancelled()){ + fragmentBos.close(); + bis.close(); + boolean isDeleted = folder.delete(); + File[] chArrToDel = folder.listFiles(); + if (! isDeleted && chArrToDel != null){ + isDeleted = true; + for (File chunkFile : chArrToDel) + isDeleted &= chunkFile.delete(); + isDeleted &= folder.delete(); + } + logPrinter.print("Split task interrupted and folder "+(isDeleted?"deleted.":"is not deleted."), EMsgType.PASS); + logPrinter.close(); + return false; + } + + chunk = new byte[4194240]; + + if ((readBytesCnt = bis.read(chunk)) < 4194240){ + if (readBytesCnt > 0) + fragmentBos.write(chunk, 0, readBytesCnt); + fragmentBos.close(); + logPrinter.updateProgress(1.0); + break main_loop; + } + + fragmentBos.write(chunk); + + logPrinter.updateProgress(chunkPercent * totalSizeCnt); + counter++; // NOTE: here we have some redundancy of variables. It has to be fixed one day. + totalSizeCnt++; + } + fragmentBos.close(); + } + + bis.close(); + + //=============== let's check what we have ============== + logPrinter.print("Original file size: "+originalFileLen, EMsgType.INFO); + long totalChunksSize = 0; + File[] chunkFileArr = folder.listFiles(); + + if (chunkFileArr == null) { + logPrinter.print("Unable to check results. It means that something went wrong.", EMsgType.FAIL); + return false; + } + else { + Arrays.sort(chunkFileArr); + for (File chunkFile : chunkFileArr) { + logPrinter.print("Chunk " + chunkFile.getName() + " size: " + chunkFile.length(), EMsgType.INFO); + totalChunksSize += chunkFile.length(); + } + + logPrinter.print("Total chunks size: " + totalChunksSize, EMsgType.INFO); + + if (originalFileLen != totalChunksSize) + logPrinter.print("Sizes are different! Do NOT use this file for installations!", EMsgType.FAIL); + else + logPrinter.print("Sizes are the same! Split file should be good!", EMsgType.PASS); + } + } + catch (Exception e){ + e.printStackTrace(); + logPrinter.print("Error: "+e.getMessage(), EMsgType.FAIL); + } + logPrinter.print("Split task complete!", EMsgType.INFO); + logPrinter.close(); + + return true; + } +} + +class MergeTask extends Task<Boolean> { + + private LogPrinter logPrinter; + private String saveToPath; + private String filePath; + + MergeTask(String filePath, String saveToPath) { + this.filePath = filePath; + this.saveToPath = saveToPath; + logPrinter = new LogPrinter(EModule.SPLIT_MERGE_TOOL); + } + @Override + protected Boolean call() { + logPrinter.print("Merge file: "+filePath, EMsgType.INFO); + + File folder = new File(filePath); + + long cnkTotalSize = 0; + + File[] chunkFiles = folder.listFiles((file, s) -> s.matches("^[0-9][0-9]$")); + + if (chunkFiles == null){ + logPrinter.print("Selected folder doesn't have any chunks. Nothing to do here.", EMsgType.FAIL); + logPrinter.close(); + return false; + } + + Arrays.sort(chunkFiles); + + logPrinter.print("Next files will be merged in following order: ", EMsgType.INFO); + for (File cnk : chunkFiles){ + logPrinter.print(" "+cnk.getName(), EMsgType.INFO); + cnkTotalSize += cnk.length(); + } + + double chunkPercent = (4194240.0 / (cnkTotalSize / 100.0) / 100.0); + long totalSizeCnt = 0; + + File resultFile = new File(saveToPath+File.separator+"!_"+folder.getName()); + //******* + for (int i = 0; ; i++){ + if (this.isCancelled()){ + logPrinter.print("Split task interrupted!", EMsgType.PASS); + logPrinter.close(); + return false; + } + + if (resultFile.exists()){ + if (i >= 50){ + logPrinter.print("Can't create new file.", EMsgType.FAIL); + logPrinter.close(); + return false; + } + + logPrinter.print("Trying to create a good new file...", EMsgType.WARNING); + resultFile = new File(saveToPath+File.separator+"!_"+i+"_"+folder.getName()); + continue; + } + logPrinter.print("Save results to: "+resultFile.getAbsolutePath(), EMsgType.INFO); + break; + } + //******* + + try { + BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(resultFile)); + + BufferedInputStream bis; + byte[] chunk; + int readBytesCnt; + + for (File cnk : chunkFiles){ + bis = new BufferedInputStream(new FileInputStream(cnk)); + while (true){ + + if (this.isCancelled()){ + bos.close(); + bis.close(); + boolean isDeleted = resultFile.delete(); + logPrinter.print("Split task interrupted and file "+(isDeleted?"deleted.":"is not deleted."), EMsgType.PASS); + logPrinter.close(); + return false; + } + + chunk = new byte[4194240]; + readBytesCnt = bis.read(chunk); + + logPrinter.updateProgress(chunkPercent * totalSizeCnt); + totalSizeCnt++; + + if (readBytesCnt < 4194240){ + if (readBytesCnt > 0) + bos.write(chunk, 0, readBytesCnt); + break; + } + + bos.write(chunk); + } + bis.close(); + } + bos.close(); + //=============== let's check what we have ============== + long resultFileSize = resultFile.length(); + logPrinter.print("Total chunks size: " + cnkTotalSize, EMsgType.INFO); + logPrinter.print("Merged file size: " + resultFileSize, EMsgType.INFO); + + if (cnkTotalSize != resultFileSize){ + logPrinter.print("Sizes are different! Do NOT use this file for installations!", EMsgType.FAIL); + return false; + } + logPrinter.print("Sizes are the same! Split file should be good!", EMsgType.PASS); + } + catch (Exception e){ + e.printStackTrace(); + logPrinter.print("Error: "+e.getMessage(), EMsgType.FAIL); + } + + logPrinter.print("Merge task complete!", EMsgType.INFO); + logPrinter.close(); + return true; + } +} \ No newline at end of file diff --git a/src/main/resources/FrontTab.fxml b/src/main/resources/FrontTab.fxml index c0383c9..6cf6935 100644 --- a/src/main/resources/FrontTab.fxml +++ b/src/main/resources/FrontTab.fxml @@ -15,7 +15,7 @@ <?import javafx.scene.layout.VBox?> <?import javafx.scene.shape.SVGPath?> -<AnchorPane xmlns="http://javafx.com/javafx/8.0.141" xmlns:fx="http://javafx.com/fxml/1" fx:controller="nsusbloader.Controllers.FrontController"> +<AnchorPane fx:id="usbNetPane" xmlns="http://javafx.com/javafx/8.0.141" xmlns:fx="http://javafx.com/fxml/1" fx:controller="nsusbloader.Controllers.FrontController"> <children> <VBox layoutX="10.0" layoutY="10.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0"> <children> diff --git a/src/main/resources/NSLMain.fxml b/src/main/resources/NSLMain.fxml index 05c6355..8d54217 100644 --- a/src/main/resources/NSLMain.fxml +++ b/src/main/resources/NSLMain.fxml @@ -23,6 +23,16 @@ <SVGPath content="M21,19V17H8V19H21M21,13V11H8V13H21M8,7H21V5H8V7M4,5V7H6V5H4M3,5A1,1 0 0,1 4,4H6A1,1 0 0,1 7,5V7A1,1 0 0,1 6,8H4A1,1 0 0,1 3,7V5M4,11V13H6V11H4M3,11A1,1 0 0,1 4,10H6A1,1 0 0,1 7,11V13A1,1 0 0,1 6,14H4A1,1 0 0,1 3,13V11M4,17V19H6V17H4M3,17A1,1 0 0,1 4,16H6A1,1 0 0,1 7,17V19A1,1 0 0,1 6,20H4A1,1 0 0,1 3,19V17Z" /> </graphic> </Tab> + <Tab closable="false"> + <content> + <AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0"> + <fx:include fx:id="SplitMergeTab" source="SplitMergeTab.fxml" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" VBox.vgrow="ALWAYS" /> + </AnchorPane> + </content> + <graphic> + <SVGPath content="M 2.4003906 2 C 1.0683906 2 2.9605947e-16 3.1125 0 4.5 L 0 19.5 A 2.4 2.5 0 0 0 2.4003906 22 L 21.599609 22 A 2.4 2.5 0 0 0 24 19.5 L 24 7 C 24 5.6125 22.919609 4.5 21.599609 4.5 L 12 4.5 L 9.5996094 2 L 2.4003906 2 z M 9 5 L 13 8.5 L 9 12 L 9 10 L 6 10 L 6 7 L 9 7 L 9 5 z M 5 9 L 5 11 L 8 11 L 8 14 L 5 14 L 5 16 L 1 12.5 L 5 9 z M 13.193359 10.962891 C 14.113498 10.962891 14.814236 11.348741 15.296875 12.123047 C 15.779514 12.89388 16.021484 13.935113 16.021484 15.244141 C 16.021484 16.556641 15.779514 17.598741 15.296875 18.373047 C 14.814236 19.14388 14.113498 19.529297 13.193359 19.529297 C 12.276693 19.529297 11.575955 19.14388 11.089844 18.373047 C 10.607205 17.598741 10.365234 16.556641 10.365234 15.244141 C 10.365234 13.935113 10.607205 12.89388 11.089844 12.123047 C 11.575955 11.348741 12.276693 10.962891 13.193359 10.962891 z M 19.589844 10.962891 C 20.509983 10.962891 21.21072 11.348741 21.693359 12.123047 C 22.175998 12.89388 22.417969 13.935113 22.417969 15.244141 C 22.417969 16.556641 22.175998 17.598741 21.693359 18.373047 C 21.21072 19.14388 20.509983 19.529297 19.589844 19.529297 C 18.673177 19.529297 17.970486 19.14388 17.484375 18.373047 C 17.001736 17.598741 16.761719 16.556641 16.761719 15.244141 C 16.761719 13.935113 17.001736 12.89388 17.484375 12.123047 C 17.970486 11.348741 18.673177 10.962891 19.589844 10.962891 z M 13.193359 11.769531 C 12.613498 11.769531 12.173177 12.092448 11.871094 12.738281 C 11.56901 13.380642 11.417969 14.195964 11.417969 15.185547 C 11.417969 15.411241 11.423611 15.655599 11.4375 15.916016 C 11.451389 16.176432 11.511068 16.528212 11.615234 16.972656 L 14.412109 12.591797 C 14.235026 12.26888 14.042318 12.052517 13.833984 11.941406 C 13.629123 11.826823 13.415582 11.769531 13.193359 11.769531 z M 19.589844 11.769531 C 19.009983 11.769531 18.567708 12.092448 18.265625 12.738281 C 17.963542 13.380642 17.8125 14.195964 17.8125 15.185547 C 17.8125 15.411241 17.820095 15.655599 17.833984 15.916016 C 17.847873 16.176432 17.907552 16.528212 18.011719 16.972656 L 20.808594 12.591797 C 20.63151 12.26888 20.438802 12.052517 20.230469 11.941406 C 20.025608 11.826823 19.812066 11.769531 19.589844 11.769531 z M 14.761719 13.556641 L 11.984375 17.962891 C 12.133681 18.216363 12.305556 18.406684 12.5 18.535156 C 12.694444 18.660156 12.91276 18.722656 13.152344 18.722656 C 13.812066 18.722656 14.280816 18.355252 14.558594 17.619141 C 14.836372 16.879557 14.974609 16.059462 14.974609 15.160156 C 14.974609 14.604601 14.90408 14.07053 14.761719 13.556641 z M 21.15625 13.556641 L 18.380859 17.962891 C 18.530165 18.216363 18.70204 18.406684 18.896484 18.535156 C 19.090929 18.660156 19.307292 18.722656 19.546875 18.722656 C 20.206597 18.722656 20.675347 18.355252 20.953125 17.619141 C 21.230903 16.879557 21.371094 16.059462 21.371094 15.160156 C 21.371094 14.604601 21.298611 14.07053 21.15625 13.556641 z" /> + </graphic> + </Tab> <Tab closable="false"> <content> <fx:include fx:id="SettingsTab" source="SettingsTab.fxml" VBox.vgrow="ALWAYS" /> @@ -47,16 +57,6 @@ <SVGPath content="M9,22A1,1 0 0,1 8,21V18H4A2,2 0 0,1 2,16V4C2,2.89 2.9,2 4,2H20A2,2 0 0,1 22,4V16A2,2 0 0,1 20,18H13.9L10.2,21.71C10,21.9 9.75,22 9.5,22V22H9M10,16V19.08L13.08,16H20V4H4V16H10M17,11H15V9H17V11M13,11H11V9H13V11M9,11H7V9H9V11Z" /> </graphic> </Tab> - <Tab closable="false"> - <content> - <AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0"> - <fx:include fx:id="SplitMergeTab" source="SplitMergeTab.fxml" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" VBox.vgrow="ALWAYS" /> - </AnchorPane> - </content> - <graphic> - <SVGPath content="M 2.4003906 2 C 1.0683906 2 2.9605947e-16 3.1125 0 4.5 L 0 19.5 A 2.4 2.5 0 0 0 2.4003906 22 L 21.599609 22 A 2.4 2.5 0 0 0 24 19.5 L 24 7 C 24 5.6125 22.919609 4.5 21.599609 4.5 L 12 4.5 L 9.5996094 2 L 2.4003906 2 z M 9 5 L 13 8.5 L 9 12 L 9 10 L 6 10 L 6 7 L 9 7 L 9 5 z M 5 9 L 5 11 L 8 11 L 8 14 L 5 14 L 5 16 L 1 12.5 L 5 9 z M 13.193359 10.962891 C 14.113498 10.962891 14.814236 11.348741 15.296875 12.123047 C 15.779514 12.89388 16.021484 13.935113 16.021484 15.244141 C 16.021484 16.556641 15.779514 17.598741 15.296875 18.373047 C 14.814236 19.14388 14.113498 19.529297 13.193359 19.529297 C 12.276693 19.529297 11.575955 19.14388 11.089844 18.373047 C 10.607205 17.598741 10.365234 16.556641 10.365234 15.244141 C 10.365234 13.935113 10.607205 12.89388 11.089844 12.123047 C 11.575955 11.348741 12.276693 10.962891 13.193359 10.962891 z M 19.589844 10.962891 C 20.509983 10.962891 21.21072 11.348741 21.693359 12.123047 C 22.175998 12.89388 22.417969 13.935113 22.417969 15.244141 C 22.417969 16.556641 22.175998 17.598741 21.693359 18.373047 C 21.21072 19.14388 20.509983 19.529297 19.589844 19.529297 C 18.673177 19.529297 17.970486 19.14388 17.484375 18.373047 C 17.001736 17.598741 16.761719 16.556641 16.761719 15.244141 C 16.761719 13.935113 17.001736 12.89388 17.484375 12.123047 C 17.970486 11.348741 18.673177 10.962891 19.589844 10.962891 z M 13.193359 11.769531 C 12.613498 11.769531 12.173177 12.092448 11.871094 12.738281 C 11.56901 13.380642 11.417969 14.195964 11.417969 15.185547 C 11.417969 15.411241 11.423611 15.655599 11.4375 15.916016 C 11.451389 16.176432 11.511068 16.528212 11.615234 16.972656 L 14.412109 12.591797 C 14.235026 12.26888 14.042318 12.052517 13.833984 11.941406 C 13.629123 11.826823 13.415582 11.769531 13.193359 11.769531 z M 19.589844 11.769531 C 19.009983 11.769531 18.567708 12.092448 18.265625 12.738281 C 17.963542 13.380642 17.8125 14.195964 17.8125 15.185547 C 17.8125 15.411241 17.820095 15.655599 17.833984 15.916016 C 17.847873 16.176432 17.907552 16.528212 18.011719 16.972656 L 20.808594 12.591797 C 20.63151 12.26888 20.438802 12.052517 20.230469 11.941406 C 20.025608 11.826823 19.812066 11.769531 19.589844 11.769531 z M 14.761719 13.556641 L 11.984375 17.962891 C 12.133681 18.216363 12.305556 18.406684 12.5 18.535156 C 12.694444 18.660156 12.91276 18.722656 13.152344 18.722656 C 13.812066 18.722656 14.280816 18.355252 14.558594 17.619141 C 14.836372 16.879557 14.974609 16.059462 14.974609 15.160156 C 14.974609 14.604601 14.90408 14.07053 14.761719 13.556641 z M 21.15625 13.556641 L 18.380859 17.962891 C 18.530165 18.216363 18.70204 18.406684 18.896484 18.535156 C 19.090929 18.660156 19.307292 18.722656 19.546875 18.722656 C 20.206597 18.722656 20.675347 18.355252 20.953125 17.619141 C 21.230903 16.879557 21.371094 16.059462 21.371094 15.160156 C 21.371094 14.604601 21.298611 14.07053 21.15625 13.556641 z" /> - </graphic> - </Tab> </tabs> </TabPane> <ProgressBar fx:id="progressBar" prefWidth="Infinity" progress="0.0"> diff --git a/src/main/resources/SplitMergeTab.fxml b/src/main/resources/SplitMergeTab.fxml index 6c142ef..9677b23 100644 --- a/src/main/resources/SplitMergeTab.fxml +++ b/src/main/resources/SplitMergeTab.fxml @@ -10,7 +10,7 @@ <?import javafx.scene.layout.VBox?> <?import javafx.scene.text.Font?> -<VBox prefHeight="190.0" spacing="25.0" xmlns="http://javafx.com/javafx/8.0.141" xmlns:fx="http://javafx.com/fxml/1" fx:controller="nsusbloader.Controllers.SplitMergeController"> +<VBox fx:id="smToolPane" spacing="25.0" xmlns="http://javafx.com/javafx/8.0.141" xmlns:fx="http://javafx.com/fxml/1" fx:controller="nsusbloader.Controllers.SplitMergeController"> <Pane minHeight="-Infinity" prefHeight="10.0" style="-fx-background-color: linear-gradient(from 41px 34px to 50px 50px, reflect, #00c8fc 40%, transparent 45%);" /> <HBox alignment="CENTER"> <children> @@ -52,6 +52,12 @@ <Button fx:id="changeSaveToBtn" contentDisplay="TOP" mnemonicParsing="false" text="%tabSplMrg_Btn_ChangeSaveToLocation" /> </children> </VBox> + <HBox alignment="CENTER"> + <children> + <Label fx:id="statusLbl" /> + </children> + </HBox> + <Pane VBox.vgrow="ALWAYS" /> <HBox alignment="CENTER"> <children> <Button fx:id="convertBtn" contentDisplay="TOP" mnemonicParsing="false" text="%tabSplMrg_Btn_Convert" /> diff --git a/src/main/resources/locale.properties b/src/main/resources/locale.properties index 204fb2e..428b0b7 100644 --- a/src/main/resources/locale.properties +++ b/src/main/resources/locale.properties @@ -58,3 +58,5 @@ tabSplMrg_Btn_ChangeSaveToLocation=Change tabSplMrg_Btn_Convert=Convert windowTitleError=Error windowBodyPleaseFinishTransfersFirst=Unable to split/merge files when application USB/Network process active. Please interrupt active transfers first. +done_txt=Done! +failure_txt=Failed diff --git a/src/main/resources/locale_rus.properties b/src/main/resources/locale_rus.properties index 46911e7..98d35aa 100644 --- a/src/main/resources/locale_rus.properties +++ b/src/main/resources/locale_rus.properties @@ -58,4 +58,6 @@ tabSplMrg_Btn_SelectFile=\u0412\u044B\u0431\u0440\u0430\u0442\u044C \u0444\u0430 tabSplMrg_Btn_SelectFolder=\u0412\u044B\u0431\u0440\u0430\u0442\u044C \u043F\u0430\u043F\u043A\u0443 windowTitleError=\u041E\u0448\u0438\u0431\u043A\u0430 windowBodyPleaseFinishTransfersFirst=\u041D\u0435\u0432\u043E\u0437\u043C\u043E\u0436\u043D\u043E \u043F\u0440\u043E\u0438\u0437\u0432\u043E\u0434\u0438\u0442\u044C \u0440\u0430\u0437\u0431\u0438\u0432\u043A\u0443 \u0438\u043B\u0438 \u0441\u043B\u0438\u044F\u043D\u0438\u0435 \u0444\u0430\u0439\u043B\u0430 \u0432 \u0442\u043E\u0442 \u043C\u043E\u043C\u0435\u043D\u0442, \u043A\u043E\u0433\u0434\u0430 \u0430\u043A\u0442\u0438\u0432\u0435\u043D \u043F\u0440\u043E\u0446\u0435\u0441\u0441 USB \u0438\u043B\u0438 \u0421\u0435\u0442\u0435\u0432\u043E\u0439 \u043F\u0435\u0440\u0435\u0434\u0430\u0447\u0438. \u0421\u043F\u0435\u0440\u0432\u0430 \u043F\u0440\u0435\u0440\u0432\u0438\u0442\u0435 \u0435\u0433\u043E. +done_txt=\u0413\u043E\u0442\u043E\u0432\u043E! +failure_txt=\u041D\u0435\u0443\u0434\u0430\u0447\u0430 diff --git a/src/main/resources/locale_ukr.properties b/src/main/resources/locale_ukr.properties index 7f264f7..4d82f1c 100644 --- a/src/main/resources/locale_ukr.properties +++ b/src/main/resources/locale_ukr.properties @@ -57,4 +57,6 @@ tabSplMrg_Btn_ChangeSaveToLocation=\u0417\u043C\u0456\u043D\u0438\u0442\u0438 tabSplMrg_Btn_SelectFile=\u0412\u0438\u0431\u0440\u0430\u0442\u0438 \u0444\u0430\u0439\u043B tabSplMrg_Btn_SelectFolder=\u0412\u0438\u0431\u0440\u0430\u0442\u0438 \u043F\u0430\u043F\u043A\u0443 windowTitleError=\u041F\u043E\u043C\u0438\u043B\u043A\u0430 -windowBodyPleaseFinishTransfersFirst=\u041D\u0435\u043C\u043E\u0436\u043B\u0438\u0432\u043E \u0437\u0434\u0456\u0439\u0441\u043D\u044E\u0432\u0430\u0442\u0438 \u0440\u043E\u0437\u0431\u0438\u0432\u043A\u0443 \u0430\u0431\u043E \u0437'\u0454\u0434\u043D\u0430\u043D\u043D\u044F \u0444\u0430\u0439\u043B\u0443 \u0443 \u0442\u043E\u0439 \u043C\u043E\u043C\u0435\u043D\u0442, \u044F\u043A \u043F\u0440\u043E\u0446\u0435\u0441 \u043F\u0435\u0440\u0435\u0434\u0430\u0447\u0456 \u0447\u0435\u0440\u0435\u0437 USB \u0447\u0438 \u0442\u043E \u0447\u0435\u0440\u0435\u0437 \u043C\u0435\u0440\u0435\u0436\u0443 \u0449\u0435 \u0430\u043A\u0442\u0438\u0432\u043D\u0438\u0439. \u0421\u043F\u043E\u0447\u0430\u0442\u043A\u0443 \u043F\u0440\u0438\u043F\u0438\u043D\u0456\u0442\u044C \u0439\u043E\u0433\u043E. \ No newline at end of file +windowBodyPleaseFinishTransfersFirst=\u041D\u0435\u043C\u043E\u0436\u043B\u0438\u0432\u043E \u0437\u0434\u0456\u0439\u0441\u043D\u044E\u0432\u0430\u0442\u0438 \u0440\u043E\u0437\u0431\u0438\u0432\u043A\u0443 \u0430\u0431\u043E \u0437'\u0454\u0434\u043D\u0430\u043D\u043D\u044F \u0444\u0430\u0439\u043B\u0443 \u0443 \u0442\u043E\u0439 \u043C\u043E\u043C\u0435\u043D\u0442, \u044F\u043A \u043F\u0440\u043E\u0446\u0435\u0441 \u043F\u0435\u0440\u0435\u0434\u0430\u0447\u0456 \u0447\u0435\u0440\u0435\u0437 USB \u0447\u0438 \u0442\u043E \u0447\u0435\u0440\u0435\u0437 \u043C\u0435\u0440\u0435\u0436\u0443 \u0449\u0435 \u0430\u043A\u0442\u0438\u0432\u043D\u0438\u0439. \u0421\u043F\u043E\u0447\u0430\u0442\u043A\u0443 \u043F\u0440\u0438\u043F\u0438\u043D\u0456\u0442\u044C \u0439\u043E\u0433\u043E. +done_txt=\u0413\u043E\u0442\u043E\u0432\u043E! +failure_txt=\u041D\u0435\u0432\u0434\u0430\u0447\u0430 \ No newline at end of file