Try raw file name before percent encoding it (closes #536)
This commit is contained in:
@@ -55,10 +55,12 @@ import javax.net.ssl.TrustManagerFactory;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.net.URLDecoder;
|
import java.net.URLDecoder;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.StandardOpenOption;
|
||||||
import java.nio.file.attribute.FileAttribute;
|
import java.nio.file.attribute.FileAttribute;
|
||||||
import java.security.KeyManagementException;
|
import java.security.KeyManagementException;
|
||||||
import java.security.KeyStore;
|
import java.security.KeyStore;
|
||||||
@@ -169,8 +171,45 @@ public class Utils {
|
|||||||
|
|
||||||
private static final PercentEscaper REPLAY_NAME_ENCODER = new PercentEscaper(".-_ ", false);
|
private static final PercentEscaper REPLAY_NAME_ENCODER = new PercentEscaper(".-_ ", false);
|
||||||
|
|
||||||
public static String replayNameToFileName(String replayName) {
|
public static Path replayNameToPath(Path folder, String replayName) {
|
||||||
return REPLAY_NAME_ENCODER.escape(replayName) + ".mcpr";
|
// If we can, prefer directly using the replay name as the file name
|
||||||
|
if (isUsable(folder, replayName + ".mcpr")) {
|
||||||
|
return folder.resolve(replayName + ".mcpr");
|
||||||
|
} else {
|
||||||
|
// otherwise, fall back to percent encoding
|
||||||
|
return folder.resolve(REPLAY_NAME_ENCODER.escape(replayName) + ".mcpr");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether a given file name is actually usable with the file system / operating system at the given folder.
|
||||||
|
*/
|
||||||
|
private static boolean isUsable(Path folder, String fileName) {
|
||||||
|
Path path = folder.resolve(fileName);
|
||||||
|
if (Files.exists(path)) {
|
||||||
|
return true; // if it already exits, it's definitely usable
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, there's no sure way to know, so we just gotta try
|
||||||
|
try (OutputStream outputStream = Files.newOutputStream(path, StandardOpenOption.CREATE_NEW)) {
|
||||||
|
outputStream.flush();
|
||||||
|
} catch (IOException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Looking good, but now we gotta clean up that mess (and Anti-Virus / Cloud Sync are know to lock them)
|
||||||
|
int attempts = 0;
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
Files.delete(path);
|
||||||
|
return true;
|
||||||
|
} catch (IOException e) {
|
||||||
|
if (attempts++ > 100) {
|
||||||
|
LOGGER.warn("Repeatedly failed to clean up temporary test file at " + path + ": ", e);
|
||||||
|
return false; // while we were able to use it, it's taken now and we can't get it back
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String fileNameToReplayName(String fileName) {
|
public static String fileNameToReplayName(String fileName) {
|
||||||
|
|||||||
@@ -155,9 +155,9 @@ public class GuiSavingReplay {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
Path replaysFolder = core.folders.getReplayFolder();
|
Path replaysFolder = core.folders.getReplayFolder();
|
||||||
Path newPath = replaysFolder.resolve(Utils.replayNameToFileName(newName));
|
Path newPath = Utils.replayNameToPath(replaysFolder, newName);
|
||||||
for (int i = 1; Files.exists(newPath); i++) {
|
for (int i = 1; Files.exists(newPath); i++) {
|
||||||
newPath = replaysFolder.resolve(Utils.replayNameToFileName(newName + " (" + i + ")"));
|
newPath = Utils.replayNameToPath(replaysFolder, newName + " (" + i + ")");
|
||||||
}
|
}
|
||||||
Files.move(path, newPath);
|
Files.move(path, newPath);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
|||||||
@@ -127,7 +127,7 @@ public class ConnectionEventHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String name = sdf.format(Calendar.getInstance().getTime());
|
String name = sdf.format(Calendar.getInstance().getTime());
|
||||||
Path outputPath = core.folders.getRecordingFolder().resolve(Utils.replayNameToFileName(name));
|
Path outputPath = Utils.replayNameToPath(core.folders.getRecordingFolder(), name);
|
||||||
ReplayFile replayFile = core.files.open(outputPath);
|
ReplayFile replayFile = core.files.open(outputPath);
|
||||||
|
|
||||||
replayFile.writeModInfo(ModCompat.getInstalledNetworkMods());
|
replayFile.writeModInfo(ModCompat.getInstalledNetworkMods());
|
||||||
|
|||||||
@@ -49,6 +49,8 @@ import java.io.File;
|
|||||||
import java.io.FileFilter;
|
import java.io.FileFilter;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@@ -132,8 +134,8 @@ public class GuiReplayViewer extends GuiScreen {
|
|||||||
public final GuiButton renameButton = new GuiButton().onClick(new Runnable() {
|
public final GuiButton renameButton = new GuiButton().onClick(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
final File file = list.getSelected().get(0).file;
|
final Path path = list.getSelected().get(0).file.toPath();
|
||||||
String name = Utils.fileNameToReplayName(file.getName());
|
String name = Utils.fileNameToReplayName(path.getFileName().toString());
|
||||||
final GuiTextField nameField = new GuiTextField().setSize(200, 20).setFocused(true).setText(name);
|
final GuiTextField nameField = new GuiTextField().setSize(200, 20).setFocused(true).setText(name);
|
||||||
final GuiYesNoPopup popup = GuiYesNoPopup.open(GuiReplayViewer.this,
|
final GuiYesNoPopup popup = GuiYesNoPopup.open(GuiReplayViewer.this,
|
||||||
new GuiLabel().setI18nText("replaymod.gui.viewer.rename.name").setColor(Colors.BLACK),
|
new GuiLabel().setI18nText("replaymod.gui.viewer.rename.name").setColor(Colors.BLACK),
|
||||||
@@ -149,16 +151,16 @@ public class GuiReplayViewer extends GuiScreen {
|
|||||||
}
|
}
|
||||||
}).onTextChanged(obj -> {
|
}).onTextChanged(obj -> {
|
||||||
popup.getYesButton().setEnabled(!nameField.getText().isEmpty()
|
popup.getYesButton().setEnabled(!nameField.getText().isEmpty()
|
||||||
&& !new File(file.getParentFile(), Utils.replayNameToFileName(nameField.getText())).exists());
|
&& Files.notExists(Utils.replayNameToPath(path.getParent(), nameField.getText())));
|
||||||
});
|
});
|
||||||
popup.onAccept(() -> {
|
popup.onAccept(() -> {
|
||||||
// Sanitize their input
|
// Sanitize their input
|
||||||
String newName = nameField.getText().trim();
|
String newName = nameField.getText().trim();
|
||||||
// This file is what they want
|
// This file is what they want
|
||||||
File targetFile = new File(file.getParentFile(), Utils.replayNameToFileName(newName));
|
Path targetPath = Utils.replayNameToPath(path.getParent(), newName);
|
||||||
try {
|
try {
|
||||||
// Finally, try to move it
|
// Finally, try to move it
|
||||||
FileUtils.moveFile(file, targetFile);
|
Files.move(path, targetPath);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
// We failed (might also be their OS)
|
// We failed (might also be their OS)
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
|||||||
Reference in New Issue
Block a user