diff --git a/lib/ac-api/src/main/java/com/webcodepro/applecommander/storage/FileEntrySource.java b/lib/ac-api/src/main/java/com/webcodepro/applecommander/storage/FileEntrySource.java index f37d322b..59bed2c7 100644 --- a/lib/ac-api/src/main/java/com/webcodepro/applecommander/storage/FileEntrySource.java +++ b/lib/ac-api/src/main/java/com/webcodepro/applecommander/storage/FileEntrySource.java @@ -34,7 +34,7 @@ import java.util.stream.Collectors; public class FileEntrySource implements Source { private final FileEntry fileEntry; - private final DataBuffer buffer; + private DataBuffer buffer; private boolean changed; private final Set hints; @@ -98,6 +98,12 @@ public class FileEntrySource implements Source { changed = true; } + @Override + public void replaceAllBytes(DataBuffer data) { + buffer = data; + changed = true; + } + @Override public boolean hasChanged() { return changed; diff --git a/lib/ac-api/src/main/java/org/applecommander/archive/zip/ZipFileEntry.java b/lib/ac-api/src/main/java/org/applecommander/archive/zip/ZipFileEntry.java index 04a6eab9..6eda2f89 100644 --- a/lib/ac-api/src/main/java/org/applecommander/archive/zip/ZipFileEntry.java +++ b/lib/ac-api/src/main/java/org/applecommander/archive/zip/ZipFileEntry.java @@ -31,7 +31,6 @@ import org.applecommander.util.FileMagic; import java.io.IOException; import java.io.UncheckedIOException; import java.nio.file.attribute.FileTime; -import java.util.Date; import java.util.Objects; import java.util.Optional; import java.util.zip.ZipEntry; @@ -83,6 +82,10 @@ public class ZipFileEntry implements FileEntry { return entry.getLastModifiedTime(); } + public FileTime getLastAccessTime() { + return entry.getLastAccessTime(); + } + public long getCrc() { return entry.getCrc(); } @@ -91,6 +94,10 @@ public class ZipFileEntry implements FileEntry { return entry.getCompressedSize(); } + public String getComment() { + return entry.getComment(); + } + public int getMethod() { return entry.getMethod(); } @@ -130,8 +137,8 @@ public class ZipFileEntry implements FileEntry { .unlocked() .size(getSize()) .BIN(0x0000) - .creation(new Date(getCreationTime().toMillis())) - .modification(new Date(getLastModifiedTime().toMillis())) + .creation(getCreationTime()) + .modification(getLastModifiedTime()) .get(); } diff --git a/lib/ac-api/src/main/java/org/applecommander/archive/zip/ZipFileStore.java b/lib/ac-api/src/main/java/org/applecommander/archive/zip/ZipFileStore.java index 394372e3..4434568f 100644 --- a/lib/ac-api/src/main/java/org/applecommander/archive/zip/ZipFileStore.java +++ b/lib/ac-api/src/main/java/org/applecommander/archive/zip/ZipFileStore.java @@ -26,9 +26,11 @@ import org.applecommander.filestore.FileEntry; import org.applecommander.filestore.FileStore; import org.applecommander.source.Source; import org.applecommander.util.Container; +import org.applecommander.util.DataBuffer; import org.applecommander.util.InformationGroup; import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.UncheckedIOException; import java.nio.file.Files; @@ -39,6 +41,9 @@ import java.util.Objects; import java.util.Optional; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; +import java.util.zip.ZipOutputStream; + +import static org.applecommander.util.Shorthand.copy; public class ZipFileStore implements FileStore { private final Source source; @@ -93,12 +98,33 @@ public class ZipFileStore implements FileStore { .addLongField("Size", ZipFileEntry::getCompressedSize, Mode.DETAIL) .addPercentField("Ratio", ZipFileEntry::getCompressedSize, ZipFileEntry::getSize, "%2.0f%%", Mode.DETAIL) .addFileTimeField("Creation", ZipFileEntry::getCreationTime) + .addFileTimeField("Last Access", ZipFileEntry::getLastAccessTime, Mode.DETAIL) .addFileTimeField("Last Modified", ZipFileEntry::getLastModifiedTime) .addLongField("CRC-32", ZipFileEntry::getCrc, "%08x", Mode.DETAIL) .addStringField("Name", FileEntry::getName) .toList(); } + @Override + public void flush() { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try (ZipOutputStream zip = new ZipOutputStream(baos)) { + for (FileEntry fileEntry : entries) { + ZipFileEntry zipFileEntry = (ZipFileEntry) fileEntry; + ZipEntry zipEntry = new ZipEntry(zipFileEntry.getName()); + copy(zipFileEntry::getCreationTime, zipEntry::setCreationTime); + copy(zipFileEntry::getLastAccessTime, zipEntry::setLastAccessTime); + copy(zipFileEntry::getLastModifiedTime, zipEntry::setLastModifiedTime); + copy(zipFileEntry::getComment, zipEntry::setComment); + zip.putNextEntry(zipEntry); + zip.write(fileEntry.getDataFork().asBytes()); + } + } catch (IOException e) { + throw new UncheckedIOException(e); + } + source.replaceAllBytes(DataBuffer.wrap(baos.toByteArray())); + } + @Override public boolean can(Capability capability) { return false; diff --git a/lib/ac-api/src/main/java/org/applecommander/archive/zip/ZipFileStoreFactory.java b/lib/ac-api/src/main/java/org/applecommander/archive/zip/ZipFileStoreFactory.java index 90bd0f1a..7f6ade43 100644 --- a/lib/ac-api/src/main/java/org/applecommander/archive/zip/ZipFileStoreFactory.java +++ b/lib/ac-api/src/main/java/org/applecommander/archive/zip/ZipFileStoreFactory.java @@ -20,6 +20,7 @@ package org.applecommander.archive.zip; import org.applecommander.filestore.FileStoreFactory; +import org.applecommander.source.Source; import org.applecommander.util.DataBuffer; public class ZipFileStoreFactory implements FileStoreFactory { @@ -32,4 +33,8 @@ public class ZipFileStoreFactory implements FileStoreFactory { ctx.fileStores.add(new ZipFileStore(ctx.source)); } } + + public static ZipFileStore create(Source source) { + return new ZipFileStore(source); + } } diff --git a/lib/ac-api/src/main/java/org/applecommander/filestore/FileStore.java b/lib/ac-api/src/main/java/org/applecommander/filestore/FileStore.java index 91fcb362..affaad1a 100644 --- a/lib/ac-api/src/main/java/org/applecommander/filestore/FileStore.java +++ b/lib/ac-api/src/main/java/org/applecommander/filestore/FileStore.java @@ -42,4 +42,20 @@ public interface FileStore extends CapabilityProvider, Container, InformationPro /// Yields a list of columns to be used in this display mode. List getDisplayColumns(); + + /// Flush any pending writes back to the source. This is intended to handle archives + /// as they do not have a fixed structure to write changes. `flush` should be invoked + /// before a save. + /// + /// Example usage: + /// + /// ```java + /// FileStore fileStore = fileStoreSelection.getSelectedItem(); + /// fileStore.flush(); + /// Source source = fileStore.get(Source.class).orElseThrow(); + /// source.save(); + /// ``` + default void flush() { + // Do Nothing + } } diff --git a/lib/ac-api/src/main/java/org/applecommander/image/DiskCopyImage.java b/lib/ac-api/src/main/java/org/applecommander/image/DiskCopyImage.java index 05c1b1e1..2a05fe17 100644 --- a/lib/ac-api/src/main/java/org/applecommander/image/DiskCopyImage.java +++ b/lib/ac-api/src/main/java/org/applecommander/image/DiskCopyImage.java @@ -125,6 +125,11 @@ public class DiskCopyImage implements Source { this.source.writeBytes(HEADER_SIZE+offset, data); } + @Override + public void replaceAllBytes(DataBuffer data) { + this.source.replaceAllBytes(data); + } + @Override public boolean hasChanged() { return source.hasChanged(); diff --git a/lib/ac-api/src/main/java/org/applecommander/image/UniversalDiskImage.java b/lib/ac-api/src/main/java/org/applecommander/image/UniversalDiskImage.java index 7d195bf4..077fbd27 100644 --- a/lib/ac-api/src/main/java/org/applecommander/image/UniversalDiskImage.java +++ b/lib/ac-api/src/main/java/org/applecommander/image/UniversalDiskImage.java @@ -126,6 +126,11 @@ public class UniversalDiskImage implements Source { source.writeBytes(this.info.dataOffset + offset, data); } + @Override + public void replaceAllBytes(DataBuffer data) { + source.replaceAllBytes(data); + } + @Override public boolean hasChanged() { return source.hasChanged(); diff --git a/lib/ac-api/src/main/java/org/applecommander/source/DataBufferSource.java b/lib/ac-api/src/main/java/org/applecommander/source/DataBufferSource.java index 0e19e0e6..73618558 100644 --- a/lib/ac-api/src/main/java/org/applecommander/source/DataBufferSource.java +++ b/lib/ac-api/src/main/java/org/applecommander/source/DataBufferSource.java @@ -31,7 +31,7 @@ import java.util.Optional; import java.util.Set; public class DataBufferSource implements Source { - private final DataBuffer dataBuffer; + private DataBuffer dataBuffer; private final String name; private final Set capabilities; private final Set hints; @@ -74,6 +74,12 @@ public class DataBufferSource implements Source { changed = true; } + @Override + public void replaceAllBytes(DataBuffer data) { + dataBuffer = data; + changed = true; + } + @Override public boolean hasChanged() { return changed; diff --git a/lib/ac-api/src/main/java/org/applecommander/source/FileEntrySource.java b/lib/ac-api/src/main/java/org/applecommander/source/FileEntrySource.java index 9c13c083..7153dafa 100644 --- a/lib/ac-api/src/main/java/org/applecommander/source/FileEntrySource.java +++ b/lib/ac-api/src/main/java/org/applecommander/source/FileEntrySource.java @@ -33,7 +33,7 @@ import java.util.stream.Collectors; public class FileEntrySource implements Source { private final FileEntry fileEntry; - private final DataBuffer buffer; + private DataBuffer buffer; private boolean changed; private final Set hints; @@ -85,6 +85,12 @@ public class FileEntrySource implements Source { changed = true; } + @Override + public void replaceAllBytes(DataBuffer data) { + buffer = data; + changed = true; + } + @Override public boolean hasChanged() { return changed; diff --git a/lib/ac-api/src/main/java/org/applecommander/source/FileSource.java b/lib/ac-api/src/main/java/org/applecommander/source/FileSource.java index b22c1311..4ef14a86 100644 --- a/lib/ac-api/src/main/java/org/applecommander/source/FileSource.java +++ b/lib/ac-api/src/main/java/org/applecommander/source/FileSource.java @@ -111,6 +111,12 @@ public class FileSource implements Source { changed = true; } + @Override + public void replaceAllBytes(DataBuffer data) { + buffer = data; + changed = true; + } + @Override public boolean hasChanged() { return changed; diff --git a/lib/ac-api/src/main/java/org/applecommander/source/Source.java b/lib/ac-api/src/main/java/org/applecommander/source/Source.java index 26a9e285..ca026c99 100644 --- a/lib/ac-api/src/main/java/org/applecommander/source/Source.java +++ b/lib/ac-api/src/main/java/org/applecommander/source/Source.java @@ -74,6 +74,9 @@ public interface Source extends CapabilityProvider, HintProvider, Container, Inf /// Write bytes to this source. Updates the changed status. void writeBytes(int offset, DataBuffer data); + /// Replace ALL bytes in this Source. + void replaceAllBytes(DataBuffer data); + /// Indicates if this source has changed. boolean hasChanged(); diff --git a/lib/ac-api/src/main/java/org/applecommander/transfer/ProdosAttributes.java b/lib/ac-api/src/main/java/org/applecommander/transfer/ProdosAttributes.java index d8547c9e..21f0011d 100644 --- a/lib/ac-api/src/main/java/org/applecommander/transfer/ProdosAttributes.java +++ b/lib/ac-api/src/main/java/org/applecommander/transfer/ProdosAttributes.java @@ -22,6 +22,7 @@ package org.applecommander.transfer; import org.applecommander.util.DataBuffer; import org.applecommander.util.FileMagic; +import java.nio.file.attribute.FileTime; import java.util.Date; import java.util.Optional; @@ -102,10 +103,22 @@ public record ProdosAttributes(String name, int access, int fileType, int auxTyp this.size = size; return this; } + public Builder creation(FileTime creation) { + if (creation != null) { + this.creation = new Date(creation.toMillis()); + } + return this; + } public Builder creation(Date creation) { this.creation = creation; return this; } + public Builder modification(FileTime modified) { + if (modified != null) { + this.modified = new Date(modified.toMillis()); + } + return this; + } public Builder modification(Date modified) { this.modified = modified; return this; diff --git a/lib/ac-api/src/main/java/org/applecommander/util/Shorthand.java b/lib/ac-api/src/main/java/org/applecommander/util/Shorthand.java new file mode 100644 index 00000000..6bcee530 --- /dev/null +++ b/lib/ac-api/src/main/java/org/applecommander/util/Shorthand.java @@ -0,0 +1,14 @@ +package org.applecommander.util; + +import java.util.function.Consumer; +import java.util.function.Supplier; + +public abstract class Shorthand { + /// This is a shorthand method to only set non-null values. + public static void copy(Supplier supplier, Consumer consumer) { + T value = supplier.get(); + if (value != null) { + consumer.accept(value); + } + } +}