Partially made GameDOS writable. Added a set capability to the DisplayColumn.

This commit is contained in:
a2geek committed 2026-10-05 19:21:51 -05:00
1 parent 2c9e120547
commit e91e6d75b7
3 files changed
+81 -30

No files matched your search

@@ -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<FileEntry,Object> 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<FileEntry,Object> getValueFn, BiConsumer<FileEntry,Object> 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<Fil
return columns;
}
/**
* A helper method that allows a short-cut of 0 modes meaning ALL modes.
*/
private Builder<T> add(String name, Alignment alignment, Function<FileEntry,Object> valueFn, String fmt, Mode ...modes) {
/// A helper method that allows a short-cut of 0 modes meaning ALL modes.
private Builder<T> add(String name, Alignment alignment,
Function<FileEntry,Object> getValueFn, BiConsumer<FileEntry,Object> 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<T> add(String name, Alignment alignment, Function<FileEntry,Object> getValueFn,
String fmt, Mode... modes) {
return add(name, alignment, getValueFn, null, false, fmt, modes);
}
public Builder<T> addIntField(String name, Function<T,Integer> valueFn, Mode ...modes) {
return addIntField(name, valueFn, "%d", modes);
@@ -98,6 +98,12 @@ public record DisplayColumn(String headerText, Alignment alignment, Function<Fil
public Builder<T> addIntField(String name, Function<T,Integer> valueFn, String fmt, Mode ...modes) {
return add(name, Alignment.RIGHT, entry -> convert(entry, valueFn), fmt, modes);
}
public Builder<T> addStringField(String name, Function<T,String> getValueFn, BiConsumer<T,String> 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<T> addStringField(String name, Function<T,String> 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<Fil
T typedFileEntry = clazz.cast(fileEntry);
return valueFn.apply(typedFileEntry);
}
private <S> void convert(FileEntry fileEntry, BiConsumer<T,S> setValueFn, Class<S> valueClass, Object value) {
T typedFileEntry = clazz.cast(fileEntry);
S typedValue = valueClass.cast(value);
setValueFn.accept(typedFileEntry, typedValue);
}
private double percentage(FileEntry fileEntry, Function<T,Number> numeratorFn, Function<T,Number> denominatorFn) {
T typedFileEntry = clazz.cast(fileEntry);
Number numerator = numeratorFn.apply(typedFileEntry);
@@ -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<DataBuffer> 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()
@@ -108,8 +108,8 @@ public class GamedosFileStore implements FileStore, GamedosConstants {
@Override
public List<DisplayColumn> 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)