/* * ac - an AppleCommander command line utility * Copyright (C) 2002-2022 by Robert Greene * robgreene at users.sourceforge.net * Copyright (C) 2003-2022 by John B. Matthews * matthewsj at users.sourceforge.net * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package com.webcodepro.applecommander.ui; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintStream; import java.util.Arrays; import java.util.List; import java.util.Queue; import com.webcodepro.applecommander.storage.DirectoryEntry; import com.webcodepro.applecommander.storage.Disk; import com.webcodepro.applecommander.storage.DiskException; import com.webcodepro.applecommander.storage.FileEntry; import com.webcodepro.applecommander.storage.FileFilter; import com.webcodepro.applecommander.storage.FormattedDisk; import com.webcodepro.applecommander.storage.FormattedDisk.DiskInformation; import com.webcodepro.applecommander.storage.filters.BinaryFileFilter; import com.webcodepro.applecommander.storage.filters.HexDumpFileFilter; import com.webcodepro.applecommander.storage.os.dos33.DosFormatDisk; import com.webcodepro.applecommander.storage.os.pascal.PascalFormatDisk; import com.webcodepro.applecommander.storage.os.prodos.ProdosFormatDisk; import com.webcodepro.applecommander.storage.physical.ByteArrayImageLayout; import com.webcodepro.applecommander.storage.physical.DosOrder; import com.webcodepro.applecommander.storage.physical.ImageOrder; import com.webcodepro.applecommander.storage.physical.ProdosOrder; import com.webcodepro.applecommander.util.AppleUtil; import com.webcodepro.applecommander.util.StreamUtil; import com.webcodepro.applecommander.util.TextBundle; import com.webcodepro.applecommander.util.TranslatorStream; import io.github.applecommander.applesingle.AppleSingle; import io.github.applecommander.applesingle.ProdosFileInfo; import io.github.applecommander.bastools.api.Configuration; import io.github.applecommander.bastools.api.Parser; import io.github.applecommander.bastools.api.TokenReader; import io.github.applecommander.bastools.api.Visitors; import io.github.applecommander.bastools.api.model.Program; import io.github.applecommander.bastools.api.model.Token; /** * ac provides a command line interface to key AppleCommander functions. Text * similar to this is produced in response to the -h option. * *
 * CommandLineHelp = AppleCommander command line options [{0}]:
 * -i  <imagename> [<imagename>] display information about image(s).
 * -ls <imagename> [<imagename>] list brief directory of image(s).
 * -l  <imagename> [<imagename>] list directory of image(s).
 * -ll <imagename> [<imagename>] list detailed directory of image(s).
 * -e  <imagename> <filename> [<output>] export file from image to stdout
 *     or to an output file. 
 * -x  <imagename> [<directory>] extract all files from image to directory.
 * -g  <imagename> <filename> [<output>] get raw file from image to stdout
 *     or to an output file. 
 * -p  <imagename> <filename> <type> [[$|0x]<addr>] put stdin
 *     in filename on image, using file type and address [0x2000].
 * -d  <imagename> <filename> delete file from image.
 * -k  <imagename> <filename> lock file on image.
 * -u  <imagename> <filename> unlock file on image.
 * -n  <imagename> <volname> change volume name (ProDOS or Pascal).
 * -cc65 <imagename> <filename> <type> put stdin with cc65 header
 *       in filename on image, using file type and address from header.  DEPRECATED.
 * -dos <imagename> <filename> <type> put stdin with cc65 header
 *       in filename on image, using file type and address from header.
 * -as <imagename> [<filename>] put stdin with AppleSingle format
 *       in filename on image, using file type, address, and (optionally) name
 *       from the AppleSingle file.
 * -geos <imagename> interpret stdin as a ProDOS GEOS transfer file and place on image.
 * -dos140 <imagename> create a 140K DOS 3.3 image.
 * -pro140 <imagename> <volname> create a 140K ProDOS image.
 * -pro800 <imagename> <volname> create an 800K ProDOS image.
 * -pas140 <imagename> <volname> create a 140K Pascal image.
 * -pas800 <imagename> <volname> create an 800K Pascal image.
 * -convert <filename> <imagename> uncompress a ShrinkIt file or disk image
 *           or convert a DiskCopy 4.2 image into a ProDOS disk image.
 * -bas    <imagename> <filename> import an AppleSoft basic file from text
 *        back to it's tokenized format.
 * 
* * @author John B. Matthews * * Changed at: Dec 1, 2017 * @author Lisias Toledo */ public class ac { private static TextBundle textBundle = UiBundle.getInstance(); public static void main(String[] args) { try { if (args.length == 0) { help(); } else if ("-i".equalsIgnoreCase(args[0])) { //$NON-NLS-1$ getDiskInfo(args); } else if ("-ls".equalsIgnoreCase(args[0])) { //$NON-NLS-1$ showDirectory(DirectoryLister.text(FormattedDisk.FILE_DISPLAY_STANDARD), args); } else if ("-l".equalsIgnoreCase(args[0])) { //$NON-NLS-1$ showDirectory(DirectoryLister.text(FormattedDisk.FILE_DISPLAY_NATIVE), args); } else if ("-ll".equalsIgnoreCase(args[0])) { //$NON-NLS-1$ showDirectory(DirectoryLister.text(FormattedDisk.FILE_DISPLAY_DETAIL), args); } else if ("-lsv".equalsIgnoreCase(args[0])) { //$NON-NLS-1$ showDirectory(DirectoryLister.csv(FormattedDisk.FILE_DISPLAY_STANDARD), args); } else if ("-lv".equalsIgnoreCase(args[0])) { //$NON-NLS-1$ showDirectory(DirectoryLister.csv(FormattedDisk.FILE_DISPLAY_NATIVE), args); } else if ("-llv".equalsIgnoreCase(args[0])) { //$NON-NLS-1$ showDirectory(DirectoryLister.csv(FormattedDisk.FILE_DISPLAY_DETAIL), args); } else if ("-lsj".equalsIgnoreCase(args[0])) { //$NON-NLS-1$ showDirectory(DirectoryLister.json(FormattedDisk.FILE_DISPLAY_STANDARD), args); } else if ("-lj".equalsIgnoreCase(args[0])) { //$NON-NLS-1$ showDirectory(DirectoryLister.json(FormattedDisk.FILE_DISPLAY_NATIVE), args); } else if ("-llj".equalsIgnoreCase(args[0])) { //$NON-NLS-1$ showDirectory(DirectoryLister.json(FormattedDisk.FILE_DISPLAY_DETAIL), args); } else if ("-e".equalsIgnoreCase(args[0])) { //$NON-NLS-1$ getFile(args[1], args[2], true, (args.length > 3 ? new PrintStream(new FileOutputStream(args[3])) : System.out)); } else if ("-x".equalsIgnoreCase(args[0])) { //$NON-NLS-1$ getFiles(args[1], (args.length > 2 ? args[2] : "")); } else if ("-g".equalsIgnoreCase(args[0])) { //$NON-NLS-1$ getFile(args[1], args[2], false, (args.length > 3 ? new PrintStream(new FileOutputStream(args[3])) : System.out)); } else if ("-p".equalsIgnoreCase(args[0])) { //$NON-NLS-1$ putFile(args[1], new Name(args[2]), args[3], (args.length > 4 ? args[4] : "0x2000")); } else if ("-pt".equalsIgnoreCase(args[0])) { putTxtFileSetHighBit(args[1], new Name(args[2])); } else if ("-ptx".equalsIgnoreCase(args[0])) { putTxtFileClearHighBit(args[1], new Name(args[2])); } else if ("-d".equalsIgnoreCase(args[0])) { //$NON-NLS-1$ deleteFile(args[1], args[2]); } else if ("-k".equalsIgnoreCase(args[0])) { //$NON-NLS-1$ setFileLocked(args[1], args[2], true); } else if ("-u".equalsIgnoreCase(args[0])) { //$NON-NLS-1$ setFileLocked(args[1], args[2], false); } else if ("-n".equalsIgnoreCase(args[0])) { //$NON-NLS-1$ setDiskName(args[1], args[2]); } else if ("-cc65".equalsIgnoreCase(args[0])) { //$NON-NLS-1$ System.err.println("Note: -cc65 is deprecated. Please use -as or -dos as appropriate."); putDOS(args[1], new Name(args[2]), args[3]); } else if ("-dos".equalsIgnoreCase(args[0])) { //$NON-NLS-1$ putDOS(args[1], new Name(args[2]), args[3]); } else if ("-as".equalsIgnoreCase(args[0])) { putAppleSingle(args[1], args.length >= 3 ? args[2] : null); } else if ("-geos".equalsIgnoreCase(args[0])) { //$NON-NLS-1$ putGEOS(args[1]); } else if ("-dos140".equalsIgnoreCase(args[0])) { //$NON-NLS-1$ createDosDisk(args[1], Disk.APPLE_140KB_DISK); } else if ("-pas140".equalsIgnoreCase(args[0])) { //$NON-NLS-1$ createPasDisk(args[1], args[2], Disk.APPLE_140KB_DISK); } else if ("-pas800".equalsIgnoreCase(args[0])) { //$NON-NLS-1$ createPasDisk(args[1], args[2], Disk.APPLE_800KB_DISK); } else if ("-pro140".equalsIgnoreCase(args[0])) { //$NON-NLS-1$ createProDisk(args[1], args[2], Disk.APPLE_140KB_DISK); } else if ("-pro800".equalsIgnoreCase(args[0])) { //$NON-NLS-1$ createProDisk(args[1], args[2], Disk.APPLE_800KB_DISK); } else if ("-convert".equalsIgnoreCase(args[0])) { //$NON-NLS-1$ if (args.length > 3) convert(args[1], args[2], Integer.parseInt(args[3])); else convert(args[1], args[2]); } else if ("-bas".equalsIgnoreCase(args[0])) { putAppleSoft(args[1], args[2]); } else { help(); } } catch (Exception ex) { System.err.println(textBundle.format("CommandLineErrorMessage", //$NON-NLS-1$ ex.getLocalizedMessage())); ex.printStackTrace(); System.exit(1); } } /** * Convert the AppleSoft BASIC program from text into it's "native" tokenized format. * Note that we try to infer the BASIC type dynamically and hard-code the start address * to 0x801. */ public static void putAppleSoft(String imageName, String fileName) throws IOException, DiskException { File fakeTempSource = File.createTempFile("ac-", "bas"); fakeTempSource.deleteOnExit(); Configuration config = Configuration.builder().sourceFile(fakeTempSource).build(); Queue tokens = TokenReader.tokenize(System.in); Parser parser = new Parser(tokens); Program program = parser.parse(); byte[] data = Visitors.byteVisitor(config).dump(program); Name name = new Name(fileName); File file = new File(imageName); if (!file.canRead()){ throw new IOException("Unable to read input file named "+imageName+"."); } Disk disk = new Disk(imageName); FormattedDisk[] formattedDisks = disk.getFormattedDisks(); FormattedDisk formattedDisk = formattedDisks[0]; // Look through the supplied types and try to pick AppleSoft. Otherwise, let's try "A". String fileType = Arrays.asList(formattedDisk.getFiletypes()).stream() .filter(ft -> "A".equalsIgnoreCase(ft) || "BAS".equalsIgnoreCase(ft)) .findFirst() .orElse("A"); FileEntry entry = name.createEntry(formattedDisk); if (entry != null) { entry.setFiletype(fileType); entry.setFilename(formattedDisk.getSuggestedFilename(name.name)); entry.setFileData(data); if (entry.needsAddress()) { entry.setAddress(config.startAddress); } formattedDisk.save(); } } /** * Put fileName from the local filesytem into the file named fileOnImageName on the disk named imageName; * Note: only volume level supported; input size unlimited. */ public static void putFile(String fileName, String imageName, String fileOnImageName, String fileType, String address) throws IOException, DiskException { Name name = new Name(fileOnImageName); File file = new File(fileName); if (!file.canRead()) { throw new IOException("Unable to read input file named "+fileName+"."); // FIXME - NLS } ByteArrayOutputStream buf = new ByteArrayOutputStream(); byte[] inb = new byte[1024]; int byteCount = 0; try (InputStream is = new FileInputStream(file)) { while ((byteCount = is.read(inb)) > 0) { buf.write(inb, 0, byteCount); } Disk disk = new Disk(imageName); FormattedDisk[] formattedDisks = disk.getFormattedDisks(); FormattedDisk formattedDisk = formattedDisks[0]; FileEntry entry = name.createEntry(formattedDisk); if (entry != null) { entry.setFiletype(fileType); entry.setFilename(formattedDisk.getSuggestedFilename(name.name)); entry.setFileData(buf.toByteArray()); if (entry.needsAddress()) { entry.setAddress(stringToInt(address)); } formattedDisk.save(); } } } /** * Put <stdin>. into the file named fileName on the disk named imageName; * Note: only volume level supported; input size unlimited. */ static void putFile(String imageName, Name name, String fileType, String address) throws IOException, DiskException { putFile(imageName, name, fileType, address, System.in); } /** * Put <stdin>. as an Apple text file into the file named * fileName on the disk named imageName. */ static void putTxtFileSetHighBit(String imageName, Name name) throws IOException, DiskException { // Order on the stream is important to ensure the translated newlines have the high bit done appropriately putFile(imageName, name, "TXT", "0", TranslatorStream.builder(System.in).lfToCr().setHighBit().get()); } /** * Put <stdin>. as an Apple text file into the file named * fileName on the disk named imageName. */ static void putTxtFileClearHighBit(String imageName, Name name) throws IOException, DiskException { // Order on the stream is important to ensure the translated newlines have the high bit done appropriately putFile(imageName, name, "TXT", "0", TranslatorStream.builder(System.in).lfToCr().clearHighBit().get()); } /** * Put InputStream into the file named fileName on the disk named imageName; * Note: only volume level supported; input size unlimited. */ static void putFile(String imageName, Name name, String fileType, String address, InputStream inputStream) throws IOException, DiskException { ByteArrayOutputStream buf = new ByteArrayOutputStream(); StreamUtil.copy(inputStream, buf); Disk disk = new Disk(imageName); FormattedDisk[] formattedDisks = disk.getFormattedDisks(); if (formattedDisks == null) System.out.println("Dude, formattedDisks is null!"); FormattedDisk formattedDisk = formattedDisks[0]; if (!disk.isSDK() && !disk.isDC42()) { FileEntry entry = name.createEntry(formattedDisk); if (entry != null) { entry.setFiletype(fileType); entry.setFilename(formattedDisk.getSuggestedFilename(name.name)); entry.setFileData(buf.toByteArray()); if (entry.needsAddress()) { entry.setAddress(stringToInt(address)); } formattedDisk.save(); } else { throw new IOException("Unable to create entry..."); } } else throw new IOException(textBundle.get("CommandLineSDKReadOnly")); //$NON-NLS-1$ } /** * Put file fileName into the file named fileOnImageName on the disk named imageName; * Assume a cc65 style four-byte header with start address in bytes 0-1. */ public static void putDOS(String fileName, String imageName, String fileOnImageName, String fileType) throws IOException, DiskException { byte[] header = new byte[4]; if (System.in.read(header, 0, 4) == 4) { int address = AppleUtil.getWordValue(header, 0); putFile(fileName, imageName, fileOnImageName, fileType, Integer.toString(address)); } } /** * Put <stdin> into the file named fileName on the disk named imageName; * Assume an DOS 3.x style four-byte header with start address in bytes 0-1. */ static void putDOS(String imageName, Name name, String fileType) throws IOException, DiskException { byte[] header = new byte[4]; if (System.in.read(header, 0, 4) == 4) { int address = AppleUtil.getWordValue(header, 0); putFile(imageName, name, fileType, Integer.toString(address)); } } /** * Put file from AppleSingle format into ProDOS image. */ public static void putAppleSingle(String imageName, String fileName) throws IOException, DiskException { putAppleSingle(imageName, fileName, System.in); } /** * AppleSingle shim to allow for unit testing. */ public static void putAppleSingle(String imageName, String fileName, InputStream inputStream) throws IOException, DiskException { AppleSingle as = AppleSingle.read(inputStream); if (fileName == null) { fileName = as.getRealName(); } if (fileName == null) { throw new IOException("Please specify a file name - this AppleSingle does not have one."); } if (as.getProdosFileInfo() == null) { throw new IOException("This AppleSingle does not contain a ProDOS file."); } if (as.getDataFork() == null || as.getDataFork().length == 0) { throw new IOException("This AppleSingle does not contain a data fork."); } Name name = new Name(fileName); ProdosFileInfo info = as.getProdosFileInfo(); String fileType = ProdosFormatDisk.getFiletype(info.getFileType()); putFile(imageName, name, fileType, Integer.toString(info.getAuxType()), new ByteArrayInputStream(as.getDataFork())); } /** * Interpret <stdin> as a GEOS file and place it on the disk named imageName. * This would only make sense for a ProDOS-formatted disk. */ static void putGEOS(String imageName) throws IOException, DiskException { putFile(imageName, new Name("GEOS-Should Be ProDOS"), "GEO", "0"); //$NON-NLS-2$ $NON-NLS-3$ } /** * Delete the file named fileName from the disk named imageName. */ static void deleteFile(String imageName, String fileName) throws IOException, DiskException { Disk disk = new Disk(imageName); Name name = new Name(fileName); if (!disk.isSDK() && !disk.isDC42()) { FormattedDisk[] formattedDisks = disk.getFormattedDisks(); for (int i = 0; i < formattedDisks.length; i++) { FormattedDisk formattedDisk = formattedDisks[i]; FileEntry entry = name.getEntry(formattedDisk); if (entry != null) { entry.delete(); disk.save(); } else { System.err.println(textBundle.format( "CommandLineNoMatchMessage", name.fullName)); //$NON-NLS-1$ } } } else throw new IOException(textBundle.get("CommandLineSDKReadOnly")); } /** * Get the file named filename from the disk named imageName; the file is * filtered according to its type and sent to <stdout>. */ static void getFile(String imageName, String fileName, boolean filter, PrintStream out) throws IOException, DiskException { Disk disk = new Disk(imageName); Name name = new Name(fileName); FormattedDisk[] formattedDisks = disk.getFormattedDisks(); if (out == null) out = System.out; for (int i = 0; i < formattedDisks.length; i++) { FormattedDisk formattedDisk = formattedDisks[i]; FileEntry entry = name.getEntry(formattedDisk); if (entry != null) { if (filter) { FileFilter ff = entry.getSuggestedFilter(); if (ff instanceof BinaryFileFilter) ff = new HexDumpFileFilter(); byte[] buf = ff.filter(entry); out.write(buf, 0, buf.length); } else { byte[] buf = entry.getFileData(); out.write(buf, 0, buf.length); } } else { System.err.println(textBundle.format( "CommandLineNoMatchMessage", name.fullName)); //$NON-NLS-1$ } } } /** * Extract all files in the image according to their respective filetype. */ static void getFiles(String imageName, String directory) throws IOException, DiskException { Disk disk = new Disk(imageName); if ((directory != null) && (directory.length() > 0)) { // Add a final directory separator if the user didn't supply one if (!directory.endsWith(File.separator)) directory = directory + File.separator; } else { directory = "."+File.separator; } FormattedDisk[] formattedDisks = disk.getFormattedDisks(); for (int i = 0; i < formattedDisks.length; i++) { FormattedDisk formattedDisk = formattedDisks[i]; writeFiles(formattedDisk.getFiles(), directory); } } /** * Recursive routine to write directory and file entries. */ static void writeFiles(List files, String directory) throws IOException, DiskException { for (FileEntry entry : files) { if ((entry != null) && (!entry.isDeleted()) && (!entry.isDirectory())) { FileFilter ff = entry.getSuggestedFilter(); if (ff instanceof BinaryFileFilter) ff = new HexDumpFileFilter(); byte[] buf = ff.filter(entry); String filename = ff.getSuggestedFileName(entry); File file = new File(directory + filename); File dir = new File(directory); dir.mkdirs(); OutputStream output = new FileOutputStream(file); output.write(buf, 0, buf.length); output.close(); } else if (entry.isDirectory()) { writeFiles(((DirectoryEntry) entry).getFiles(),directory+entry.getFilename()+File.separator); } } } /** * Recursive routine to locate a specific file by filename; In the instance * of a system with directories (e.g. ProDOS), this really returns the first * file with the given filename. */ @Deprecated static FileEntry getEntry(List files, String fileName) throws DiskException { if (files != null) { for (FileEntry entry : files) { String entryName = entry.getFilename(); if (!entry.isDeleted() && fileName.equalsIgnoreCase(entryName)) { return entry; } if (entry.isDirectory()) { entry = getEntry(((DirectoryEntry) entry).getFiles(), fileName); if (entry != null) { return entry; } } } } return null; } /** * Display a directory listing of each disk in args. */ static void showDirectory(DirectoryLister dl, String[] args) throws IOException { for (String filename : Arrays.copyOfRange(args, 1, args.length)) { try { dl.list(filename); } catch (DiskException e) { throw new IOException(e); } catch (RuntimeException e) { System.out.println(filename + ": " + e.getMessage()); //$NON-NLS-1$ System.out.println(); } } } /** * Display information about each disk in args. */ static void getDiskInfo(String[] args) throws IOException, DiskException { for (int d = 1; d < args.length; d++) { Disk disk = new Disk(args[d]); FormattedDisk[] formattedDisks = disk.getFormattedDisks(); for (int i = 0; i < formattedDisks.length; i++) { FormattedDisk formattedDisk = formattedDisks[i]; for (DiskInformation diskinfo : formattedDisk.getDiskInformation()) { System.out.println(diskinfo.getLabel() + ": " + diskinfo.getValue()); } } System.out.println(); } } /** * Set the lockState of the file named fileName on the disk named imageName. * Proposed by David Schmidt. */ public static void setFileLocked(String imageName, String name, boolean lockState) throws IOException, DiskException { setFileLocked(imageName, new Name(name), lockState); } /** * Set the lockState of the file named fileName on the disk named imageName. * Proposed by David Schmidt. */ static void setFileLocked(String imageName, Name name, boolean lockState) throws IOException, DiskException { Disk disk = new Disk(imageName); if (!disk.isSDK() && !disk.isDC42()) { FormattedDisk[] formattedDisks = disk.getFormattedDisks(); for (int i = 0; i < formattedDisks.length; i++) { FormattedDisk formattedDisk = formattedDisks[i]; FileEntry entry = name.getEntry(formattedDisk); if (entry != null) { entry.setLocked(lockState); disk.save(); } else { System.err.println(textBundle.format( "CommandLineNoMatchMessage", name.fullName)); //$NON-NLS-1$ } } } else throw new IOException(textBundle.get("CommandLineSDKReadOnly")); } /** * Set the volume name for a given disk image. Only effective for ProDOS or * Pascal disks; others ignored. Proposed by David Schmidt. */ public static void setDiskName(String imageName, String volName) throws IOException, DiskException { Disk disk = new Disk(imageName); if (!disk.isSDK() && !disk.isDC42()) { FormattedDisk[] formattedDisks = disk.getFormattedDisks(); FormattedDisk formattedDisk = formattedDisks[0]; formattedDisk.setDiskName(volName); formattedDisks[0].save(); } else throw new IOException(textBundle.get("CommandLineSDKReadOnly")); } /** * Create a DOS disk image. */ public static void createDosDisk(String fileName, int imageSize) throws IOException { ByteArrayImageLayout layout = new ByteArrayImageLayout(imageSize); ImageOrder imageOrder = new DosOrder(layout); FormattedDisk[] disks = DosFormatDisk.create(fileName, imageOrder); disks[0].save(); } /** * Create a Pascal disk image. */ public static void createPasDisk(String fileName, String volName, int imageSize) throws IOException { ByteArrayImageLayout layout = new ByteArrayImageLayout(imageSize); ImageOrder imageOrder = new ProdosOrder(layout); FormattedDisk[] disks = PascalFormatDisk.create(fileName, volName, imageOrder); disks[0].save(); } /** * Create a ProDOS disk image. */ public static void createProDisk(String fileName, String volName, int imageSize) throws IOException { ByteArrayImageLayout layout = new ByteArrayImageLayout(imageSize); ImageOrder imageOrder = new ProdosOrder(layout); FormattedDisk[] disks = ProdosFormatDisk.create(fileName, volName, imageOrder); disks[0].save(); } /** * Unshrink or otherwise interpret incoming data depending on what kind it is: * * DiskCopy 4.2 image - convert it to a ProDOS image * SDK disk image - unpack it to a disk image * ShrinkIt file bundle - unpack files onto a disk image sized to fit */ static void convert(String shrinkName, String imageName) throws IOException { convert(shrinkName, imageName, 0); } /** * Unshrink or otherwise interpret incoming data depending on what kind it is: * * DiskCopy 4.2 image - convert it to a ProDOS image * SDK disk image - unpack it to a disk image * ShrinkIt file bundle - unpack files onto a disk image sized to fit, or as specified in numbers of blocks */ static void convert(String shrinkName, String imageName, int imageSize) throws IOException { Disk disk = new Disk(shrinkName, imageSize); disk.setFilename(imageName); disk.save(); } static int stringToInt(String s) { int i = 0; try { s = s.trim().toLowerCase(); if (s.startsWith("$")) { // 6502, Motorola i = Integer.parseInt(s.substring(1), 0x10); } else if (s.startsWith("0x")) { // Java, C i = Integer.parseInt(s.substring(2), 0x10); } else { i = Integer.parseInt(s); } } catch (NumberFormatException nfe) { i = 0x2000; } return i; } static void help() { System.err.println(textBundle.format( "CommandLineHelp", AppleCommander.VERSION)); //$NON-NLS-1$ } public static class Name { private String fullName; private String name; private String[] path; public Name(String s) { this.fullName = s; if (s.startsWith("/")) { fullName = s.substring(1, s.length()); } this.path = s.split("/"); this.name = path[path.length - 1]; } public FileEntry getEntry(FormattedDisk formattedDisk) throws DiskException { List files = formattedDisk.getFiles(); FileEntry entry = null; for (int i = 0; i < path.length - 1; i++) { String dirName = path[i]; for (int j = 0; j < files.size(); j++) { entry = (FileEntry) files.get(j); String entryName = entry.getFilename(); if (entry.isDirectory() && dirName.equalsIgnoreCase(entryName)) { files = ((DirectoryEntry) entry).getFiles(); } } } for (int i = 0; i < files.size(); i++) { entry = (FileEntry) files.get(i); String entryName = entry.getFilename(); if (!entry.isDeleted() && name.equalsIgnoreCase(entryName)) { return entry; } } return null; } public FileEntry createEntry(FormattedDisk formattedDisk) throws DiskException { if (path.length == 1) { return formattedDisk.createFile(); } List files = formattedDisk.getFiles(); DirectoryEntry dir = null, parentDir = null; for (int i = 0; i < path.length - 1; i++) { String dirName = path[i]; dir = null; for (int j = 0; j < files.size(); j++) { FileEntry entry = (FileEntry) files.get(j); String entryName = entry.getFilename(); if (!entry.isDeleted() && entry.isDirectory() && dirName.equalsIgnoreCase(entryName)) { dir = (DirectoryEntry) entry; parentDir = dir; files = dir.getFiles(); } } if (dir == null) { if (parentDir != null) { // If there's a parent directory in the mix, add // the new child directory to that. dir = parentDir.createDirectory(dirName); parentDir = dir; } else { // Add the directory to the root of the filesystem dir = formattedDisk.createDirectory(dirName); parentDir = dir; } } } if (dir != null) { return dir.createFile(); } else { System.err.println(textBundle.format( "CommandLineNoMatchMessage", fullName)); //$NON-NLS-1$ return null; } } } }