Skip to content

Commit

Permalink
Extend clipboard infrastructure tests
Browse files Browse the repository at this point in the history
Copy the data to a new volume for the "xfer" tests.

Added a MacZip-to-ProDOS test.
  • Loading branch information
fadden committed Nov 4, 2023
1 parent 690f5a7 commit 07eacb0
Show file tree
Hide file tree
Showing 11 changed files with 437 additions and 82 deletions.
12 changes: 9 additions & 3 deletions AppCommon/ClipFileEntry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ public sealed class ClipFileEntry {
/// opening the archive or filesystem entry and just copying data out.
/// </summary>
/// <remarks>
/// <para>This class is NOT serializable. It only exists on the local side, and is
/// invoked when the remote side requests file contents.</para>
/// <para>This class is NOT serializable. It only exists on the sending side, and is
/// invoked when the remote receiver requests file contents.</para>
/// </remarks>
public class StreamGenerator {
private object mArchiveOrFileSystem;
Expand Down Expand Up @@ -203,6 +203,8 @@ public void OutputToStream(Stream outStream) {
/// Opens one fork of a file entry, in a disk image or file archive.
/// </summary>
/// <returns>Opened stream.</returns>
/// <exception cref="InvalidOperationException">Archive or filesystem is invalid,
/// probably because the remote side closed.</exception>
private Stream OpenPart(FilePart part = FilePart.Unknown) {
if (part == FilePart.Unknown) {
part = mPart; // "default" part
Expand Down Expand Up @@ -365,7 +367,8 @@ private void GenerateAS(Stream outStream) {
}

/// <summary>
/// Stream generator.
/// Stream generator. Copies data from the source archive to a stream connected to the
/// remote receiver.
/// </summary>
/// <remarks>
/// The NonSerialized attribute can only be applied to fields, not properties.
Expand Down Expand Up @@ -445,6 +448,9 @@ public ClipFileEntry(object archiveOrFileSystem, IFileEntry entry, IFileEntry ad
Type? expectedType, AppHook appHook) {
Debug.Assert(!string.IsNullOrEmpty(attribs.FileNameOnly));
Debug.Assert(!string.IsNullOrEmpty(attribs.FullPathName));
if (entry != IFileEntry.NO_ENTRY) {
Debug.Assert(attribs.FullPathSep == entry.DirectorySeparatorChar);
}

mStreamGen = new StreamGenerator(archiveOrFileSystem, entry, adfEntry, part, attribs,
preserveMode, exportSpec, defaultSpecs, expectedType, appHook);
Expand Down
11 changes: 10 additions & 1 deletion AppCommon/ClipFileSet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ private void GenerateFromArchive(IArchive arc, List<IFileEntry> entries) {
// Generate file attributes. The same object will be used for both forks.
FileAttribs attrs = new FileAttribs(entry);
if (mStripPaths) {
// Configure extract path and attribute path.
extractPath = Path.GetFileName(extractPath);
attrs.FullPathName = PathName.GetFileName(attrs.FullPathName,
attrs.FullPathSep);
Expand Down Expand Up @@ -207,7 +208,10 @@ private static void GetMacZipAttribs(IArchive arc, IFileEntry adfEntry,
using Stream adfStream = ArcTemp.ExtractToTemp(arc, adfEntry, FilePart.DataFork);
using AppleSingle adfArchive = AppleSingle.OpenArchive(adfStream, appHook);
IFileEntry adfArchiveEntry = adfArchive.GetFirstEntry();
// Get the attributes from AppleSingle, but don't replace the filename.
string fileName = attrs.FileNameOnly;
attrs.GetFromAppleSingle(adfArchiveEntry);
attrs.FileNameOnly = fileName;
} catch (Exception ex) {
// Never mind.
appHook.LogW("Failed to get attributes from ADF header (" + adfEntry +
Expand Down Expand Up @@ -253,6 +257,7 @@ private void AddPathDirEntries(IArchive arc, string pathName, char dirSep) {
// Generate an entry for the directory.
FileAttribs attrs = new FileAttribs();
attrs.FullPathName = dirName;
attrs.FullPathSep = dirSep;
attrs.FileNameOnly = PathName.GetFileName(dirName, dirSep);
attrs.IsDirectory = true;
string extractPath = PathName.AdjustPathName(dirName, dirSep,
Expand Down Expand Up @@ -397,10 +402,10 @@ private void AddMissingDirectories(IFileSystem fs, IFileEntry entry,
// Add this directory to the output list.
FileAttribs attrs = new FileAttribs(entry);
attrs.FullPathName = ReRootedPathName(entry, aboveRootEntry);
Debug.Assert(!string.IsNullOrEmpty(attrs.FullPathName));
attrs.FullPathSep = entry.DirectorySeparatorChar;
attrs.FileNameOnly = PathName.GetFileName(attrs.FullPathName, attrs.FullPathSep);
Debug.Assert(attrs.IsDirectory);
Debug.Assert(!string.IsNullOrEmpty(attrs.FullPathName));
string extractPath = ExtractFileWorker.GetAdjPathName(entry, aboveRootEntry,
Path.DirectorySeparatorChar);
ForeignEntries.Add(new ClipFileEntry(fs, entry, IFileEntry.NO_ENTRY,
Expand Down Expand Up @@ -580,6 +585,7 @@ private void CreateForExport(object archiveOrFileSystem, IFileEntry entry,
if (expectedType == null) {
return;
}
Debug.Assert(!string.IsNullOrEmpty(attrs.FileNameOnly));
string ext;
if (expectedType == typeof(SimpleText)) {
ext = TXTGenerator.FILE_EXT;
Expand Down Expand Up @@ -645,7 +651,10 @@ private void CreateForExport(object archiveOrFileSystem, IFileEntry entry,
adfStream = ArcTemp.ExtractToTemp(arc, adfEntry, dataPart);
adfArchive = AppleSingle.OpenArchive(adfStream, appHook);
IFileEntry adfArchiveEntry = adfArchive.GetFirstEntry();
// Copy the ADF attributes out, but retain the original filename.
string fileName = attrs.FileNameOnly;
attrs.GetFromAppleSingle(adfArchiveEntry);
attrs.FileNameOnly = fileName;
if (adfArchiveEntry.HasRsrcFork && adfArchiveEntry.RsrcLength > 0) {
rsrcStream = adfArchive.OpenPart(adfArchiveEntry, FilePart.RsrcFork);
}
Expand Down
8 changes: 5 additions & 3 deletions AppCommon/ClipPasteWorker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,11 @@ public class ClipPasteWorker {
public delegate CallbackFacts.Results CallbackFunc(CallbackFacts what);

/// <summary>
/// Stream generator function interface definition.
/// Input stream generator function interface definition. This is invoked on the
/// receiving side to receive input from the sender.
/// </summary>
/// <param name="clipEntry">Entry to generate a stream for.</param>
/// <returns>Write-only, non-seekable output stream, or null if no stream is
/// <returns>Read-only, non-seekable output stream, or null if no stream is
/// available for the specified entry.</returns>
public delegate Stream? ClipStreamGenerator(ClipFileEntry clipEntry);

Expand Down Expand Up @@ -581,7 +582,8 @@ private void CopyFilePart(ClipFileEntry clipEntry, int progressPercent,

using (Stream? inStream = mClipStreamGen(clipEntry)) {
if (inStream == null) {
throw new IOException("Unable to open source stream");
throw new IOException("Unable to open source stream. " +
"Make sure source archive is open and files have not been deleted.");
}
while (true) {
int actual = inStream.Read(mCopyBuf, 0, mCopyBuf.Length);
Expand Down
4 changes: 3 additions & 1 deletion CiderPress2-notes.txt
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,9 @@ Windows Explorer.
Data is read from the virtual streams at the time the files are dropped or pasted. Because of
this, files copied onto the clipboard cannot be pasted after the source archive or disk image is
closed. Similarly, files copied to the clipboard cannot be pasted if they are deleted from a
disk image or file archive after being copied.
disk image or file archive after being copied. This means you can't copy files between disk
images by opening the first image, copying the data, closing it, and then opening the second image
to do the paste.

The application has no ability to influence how Windows Explorer handles the clipboard data
prepared for "foreign" transfers, so the file data must be arranged in the clipboard in a way that
Expand Down
7 changes: 4 additions & 3 deletions DiskArc/DAExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -167,14 +167,14 @@ public static bool CheckStorageName(this IArchive archive, string storageName) {
/// <param name="fsType">Filesystem to format the disk with.</param>
/// <param name="volumeName">IFileSystem.Format() argument.</param>
/// <param name="volumeNum">IFileSystem.Format() argument.</param>
/// <param name="makeBootable">IFileSystem.Format() argument.</param>
/// <param name="reserveBoot">IFileSystem.Format() argument.</param>
/// <param name="appHook">Application hook reference.</param>
/// <exception cref="InvalidOperationException">ChunkAccess is null, or FileSystem is
/// not null.</exception>
/// <exception cref="NotSupportedException">Filesystem not supported by this
/// function.</exception>
public static void FormatDisk(this IDiskImage diskImage, Defs.FileSystemType fsType,
string volumeName, int volumeNum, bool makeBootable, AppHook appHook) {
string volumeName, int volumeNum, bool reserveBoot, AppHook appHook) {
if (diskImage.ChunkAccess == null || diskImage.Contents != null) {
throw new InvalidOperationException(
"Disk image must have non-null ChunkAccess and no Contents");
Expand Down Expand Up @@ -208,7 +208,7 @@ public static void FormatDisk(this IDiskImage diskImage, Defs.FileSystemType fsT
diskImage.ChunkAccess.Initialize();

// Format the filesystem.
fs.Format(volumeName, volumeNum, makeBootable);
fs.Format(volumeName, volumeNum, reserveBoot);

// Dispose of the IFileSystem object to ensure everything has been written.
fs.Dispose();
Expand Down Expand Up @@ -486,6 +486,7 @@ public static IFileEntry FindFileEntry(this IFileSystem fs, IFileEntry dirEntry,
/// directory.</exception>
public static bool TryFindFileEntry(this IFileSystem fs, IFileEntry dirEntry,
string fileName, out IFileEntry entry) {
Debug.Assert(dirEntry.GetFileSystem() == fs);
if (!dirEntry.IsDirectory) {
throw new ArgumentException("Argument is not a directory");
}
Expand Down
1 change: 1 addition & 0 deletions DiskArc/FileAttribs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ public void GetFromAppleSingle(Stream asStream, AppHook appHook) {
/// <summary>
/// Overwrites attributes with values from an AppleSingle/AppleDouble file. Notably,
/// the FileNameOnly and RsrcLength fields are replaced with the value from the archive.
/// FullPathName and FullPathSep are not altered.
/// </summary>
/// <param name="entry">Entry from AppleSingle/AppleDouble archive.</param>
public void GetFromAppleSingle(IFileEntry entry) {
Expand Down
Loading

0 comments on commit 07eacb0

Please sign in to comment.