diff --git a/lib/ac-api/src/main/java/org/applecommander/filestore/DisplayColumn.java b/lib/ac-api/src/main/java/org/applecommander/filestore/DisplayColumn.java index 5ef0850c..7c12be98 100644 --- a/lib/ac-api/src/main/java/org/applecommander/filestore/DisplayColumn.java +++ b/lib/ac-api/src/main/java/org/applecommander/filestore/DisplayColumn.java @@ -27,38 +27,34 @@ import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Set; +import java.util.function.BiConsumer; import java.util.function.Function; -/** - * A DisplayColumn supports formatted output from various FileStores in a - * dynamic fashion. The intent is to provide a flexible display interface - * that does not rely on hard-coded implementations. - */ -public record DisplayColumn(String headerText, Alignment alignment, Function valueFn, String fmt, Mode... modes) { - /** - * Indicates if this DisplayColumn supports the mode. In this manner, the columns - * can be filtered out. Or it can be ignored to display everything. - */ +/// A DisplayColumn supports formatted output from various FileStores in a +/// dynamic fashion. The intent is to provide a flexible display interface +/// that does not rely on hard-coded implementations. +public record DisplayColumn(String headerText, Alignment alignment, + Function getValueFn, BiConsumer setValueFn, + boolean editInline, String fmt, Mode... modes) { + + /// Indicates if this DisplayColumn supports the mode. In this manner, the columns + /// can be filtered out. Or it can be ignored to display everything. public boolean supports(Mode mode) { return Set.of(modes).contains(mode); } - /** - * Helper method to convert the column into a formatted string for display. - */ + + /// Helper method to convert the column into a formatted string for display. public String formatAsText(FileEntry fileEntry) { - return String.format(fmt, valueFn.apply(fileEntry)); + return String.format(fmt, getValueFn.apply(fileEntry)); } - /** - * Indicates the display mode to help filter columns to users' preference. - */ + /// Indicates the display mode to help filter columns to users' preference. public enum Mode { NATIVE, DETAIL } - /** - * Indicates how this column should be aligned. - */ + + /// Indicates how this column should be aligned. public enum Alignment { LEFT, CENTER, @@ -81,16 +77,20 @@ public record DisplayColumn(String headerText, Alignment alignment, Function add(String name, Alignment alignment, Function valueFn, String fmt, Mode ...modes) { + /// A helper method that allows a short-cut of 0 modes meaning ALL modes. + private Builder add(String name, Alignment alignment, + Function getValueFn, BiConsumer setValueFn, + boolean editInline, String fmt, Mode... modes) { if (modes.length == 0) { modes = new Mode[] { Mode.NATIVE, Mode.DETAIL }; } - columns.add(new DisplayColumn(name, alignment, valueFn, fmt, modes)); + columns.add(new DisplayColumn(name, alignment, getValueFn, setValueFn, editInline, fmt, modes)); return this; } + private Builder add(String name, Alignment alignment, Function getValueFn, + String fmt, Mode... modes) { + return add(name, alignment, getValueFn, null, false, fmt, modes); + } public Builder addIntField(String name, Function valueFn, Mode ...modes) { return addIntField(name, valueFn, "%d", modes); @@ -98,6 +98,12 @@ public record DisplayColumn(String headerText, Alignment alignment, Function addIntField(String name, Function valueFn, String fmt, Mode ...modes) { return add(name, Alignment.RIGHT, entry -> convert(entry, valueFn), fmt, modes); } + public Builder addStringField(String name, Function getValueFn, BiConsumer setValueFn, + boolean editInline, Mode ...modes) { + return add(name, Alignment.LEFT, entry -> convert(entry, getValueFn), + (entry,value) -> convert(entry, setValueFn, String.class, value), + editInline, "%s", modes); + } public Builder addStringField(String name, Function valueFn, Mode ...modes) { return add(name, Alignment.LEFT, entry -> convert(entry, valueFn), "%s", modes); } @@ -124,6 +130,11 @@ public record DisplayColumn(String headerText, Alignment alignment, Function void convert(FileEntry fileEntry, BiConsumer setValueFn, Class valueClass, Object value) { + T typedFileEntry = clazz.cast(fileEntry); + S typedValue = valueClass.cast(value); + setValueFn.accept(typedFileEntry, typedValue); + } private double percentage(FileEntry fileEntry, Function numeratorFn, Function denominatorFn) { T typedFileEntry = clazz.cast(fileEntry); Number numerator = numeratorFn.apply(typedFileEntry); diff --git a/lib/ac-api/src/main/java/org/applecommander/os/gamedos/GamedosFileEntry.java b/lib/ac-api/src/main/java/org/applecommander/os/gamedos/GamedosFileEntry.java index 823513b2..f0343dbe 100644 --- a/lib/ac-api/src/main/java/org/applecommander/os/gamedos/GamedosFileEntry.java +++ b/lib/ac-api/src/main/java/org/applecommander/os/gamedos/GamedosFileEntry.java @@ -21,15 +21,16 @@ package org.applecommander.os.gamedos; import org.applecommander.device.TrackSectorDevice; import org.applecommander.filestore.Directory; -import org.applecommander.filestore.FileEntry; import org.applecommander.filestore.FileStore; import org.applecommander.filestore.ProdosAttributes; +import org.applecommander.filestore.WritableFileEntry; import org.applecommander.util.Container; import org.applecommander.util.DataBuffer; import java.util.Optional; +import java.util.function.Consumer; -public class GamedosFileEntry implements FileEntry, GamedosConstants { +public class GamedosFileEntry implements WritableFileEntry, GamedosConstants { private final GamedosDirectory directory; private final TrackSectorDevice device; private final int sector; @@ -43,7 +44,12 @@ public class GamedosFileEntry implements FileEntry, GamedosConstants { } public DataBuffer readEntry() { - return device.readSector(GamedosDirectory.DIRECTORY_TRACK, sector).slice(offset, ENTRY_SIZE); + return device.readSector(DIRECTORY_TRACK, sector).slice(offset, ENTRY_SIZE); + } + public void modifyEntry(Consumer consumer) { + DataBuffer data = device.readSector(GamedosDirectory.DIRECTORY_TRACK, sector); + consumer.accept(data); + device.writeSector(DIRECTORY_TRACK, sector, data); } @Override @@ -65,6 +71,10 @@ public class GamedosFileEntry implements FileEntry, GamedosConstants { public String getName() { return readEntry().getFixedLengthString(ENTRY_NAME_OFFSET, ENTRY_NAME_LENGTH).trim(); } + @Override + public void setName(String name) { + modifyEntry(data -> data.putFixedLengthString(ENTRY_NAME_OFFSET, ENTRY_NAME_LENGTH, name)); + } @Override public long getSize() { @@ -74,24 +84,54 @@ public class GamedosFileEntry implements FileEntry, GamedosConstants { public String getFiletype() { return readEntry().getFixedLengthString(ENTRY_TYPE_OFFSET,1); } + public void setFiletype(String filetype) { + modifyEntry(data -> data.putFixedLengthString(ENTRY_TYPE_OFFSET, 1, filetype)); + } + public int getFirstTrack() { return readEntry().getUnsignedByte(ENTRY_TRACK_OFFSET); } + public void setFirstTrack(int track) { + modifyEntry(data -> data.putByte(ENTRY_TRACK_OFFSET, track)); + } + public int getFirstSector() { return readEntry().getUnsignedByte(ENTRY_SECTOR_OFFSET); } + public void setFirstSector(int sector) { + modifyEntry(data -> data.putByte(ENTRY_SECTOR_OFFSET, sector)); + } + public int getMeta() { return readEntry().getUnsignedShort(ENTRY_META_OFFSET); } + public void setMeta(int meta) { + modifyEntry(data -> data.putShort(ENTRY_META_OFFSET, (short)meta)); + } + public int getSectorCount() { return readEntry().getUnsignedByte(ENTRY_SECTORS_OFFSET); } + public void setSectorCount(int sectorCount) { + modifyEntry(data -> data.putByte(ENTRY_SECTORS_OFFSET, sectorCount)); + } @Override public DataBuffer getDataFork() { return device.readRange(getFirstTrack(), getFirstSector(), getSectorCount()); } + @Override + public void setDataFork(DataBuffer data) { + int requiredSectors = device.calculateRequiredSectors(data.limit()); + if (getSectorCount() <= requiredSectors) { + device.writeRange(getFirstTrack(), getFirstSector(), data); + } + else { + throw new UnsupportedOperationException("TODO! can only write same size or smaller files"); + } + } + @Override public ProdosAttributes getProdosAttributes() { ProdosAttributes.Builder builder = ProdosAttributes.builder() diff --git a/lib/ac-api/src/main/java/org/applecommander/os/gamedos/GamedosFileStore.java b/lib/ac-api/src/main/java/org/applecommander/os/gamedos/GamedosFileStore.java index 02fc6dfb..573a594a 100644 --- a/lib/ac-api/src/main/java/org/applecommander/os/gamedos/GamedosFileStore.java +++ b/lib/ac-api/src/main/java/org/applecommander/os/gamedos/GamedosFileStore.java @@ -108,8 +108,8 @@ public class GamedosFileStore implements FileStore, GamedosConstants { @Override public List getDisplayColumns() { return DisplayColumn.builder(GamedosFileEntry.class) - .addStringField("Type", GamedosFileEntry::getFiletype) - .addStringField("Name", GamedosFileEntry::getName) + .addStringField("Type", GamedosFileEntry::getFiletype, GamedosFileEntry::setFiletype, false) + .addStringField("Name", GamedosFileEntry::getName, GamedosFileEntry::setName, true) .addIntField("Meta", GamedosFileEntry::getMeta, "$%04X") .addIntField("First Track", GamedosFileEntry::getFirstTrack, Mode.DETAIL) .addIntField("First Sector", GamedosFileEntry::getFirstSector, Mode.DETAIL)