/*- * Copyright (C) 2011 Erik Larsson * * 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 3 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, see <http://www.gnu.org/licenses/>. */ package org.catacombae.hfsexplorer.tools; import java.io.FileNotFoundException; import java.io.IOException; import java.io.RandomAccessFile; import java.util.Arrays; import java.util.LinkedList; import org.catacombae.storage.ps.apm.types.APMPartition; import org.catacombae.storage.ps.apm.types.DriverDescriptorRecord; import org.catacombae.util.Util; /** * @author <a href="http://www.catacombae.org/" target="_top">Erik Larsson</a> */ public class MkAPM { private static class PartitionSpec { Long partitionByteStart = null; Long partitionByteLength = null; String partitionName = null; String partitionType = null; } /* Options. */ private static class Options { Integer blockSize = null; //Long deviceBlockCount = null; final LinkedList<PartitionSpec> partitions = new LinkedList<PartitionSpec>(); RandomAccessFile file = null; } private static Options options = new Options(); private static boolean parseOptions(String[] args) { for(int i = 0; i < args.length - 1; ++i) { String curArg = args[i]; System.err.println("Processing argument " + i + ": " + curArg); if(curArg.equals("--sector-size")) { if(i + 1 == args.length - 1) { System.err.println("Incomplete sector size specification."); return false; } try { options.blockSize = Integer.parseInt(args[++i]); } catch(Exception e) { System.err.println("Error: Invalid sector size \"" + args[i] + "\"."); return false; } } /* else if(curArg.equals("--byte-count")) { try { byteCount = Long.parseLong(args[++i]); } catch(Exception e) { System.err.println("Error: Invalid byte count \"" + args[i] + "\"."); return false; } } */ else if(curArg.equals("--part")) { /* Add partition. */ if(i + 4 == args.length - 1) { System.err.println("Incomplete part specification."); return false; } PartitionSpec p = new PartitionSpec(); try { p.partitionByteStart = Long.parseLong(args[++i]); p.partitionByteLength = Long.parseLong(args[++i]); p.partitionName = args[++i]; p.partitionType = args[++i]; } catch(Exception e) { System.err.println("Error: Invalid partition " + "specification."); return false; } options.partitions.add(p); } else { System.err.println("Unrecognized argument: \"" + curArg + "\""); return false; } } String filename = args[args.length - 1]; try { options.file = new RandomAccessFile(filename, "r"); } catch(FileNotFoundException ex) { System.err.println("Error: Failed to open file \"" + filename + "\"."); return false; } if(options.blockSize == null) { System.err.println("Error: No block size specified."); return false; } { int i = 0; for(PartitionSpec ps : options.partitions) { if((ps.partitionByteStart % options.blockSize) != 0) { System.err.println("Error: Start of partition " + i + " is not a multiple of sector size."); return false; } else if((ps.partitionByteLength % options.blockSize) != 0) { System.err.println("Error: Length of partition " + i + " is not a multiple of sector size."); return false; } else if(ps.partitionName.length() > 32) { System.err.println("Error: Name of partition " + i + " is too long."); return false; } else if(ps.partitionType.length() > 32) { System.err.println("Error: Type of partition " + i + " is too long."); return false; } try { Util.encodeASCIIString(ps.partitionName); } catch(IllegalArgumentException e) { System.err.println("Error: Name of partition " + i + " contains invalid characters."); return false; } try { Util.encodeASCIIString(ps.partitionType); } catch(IllegalArgumentException e) { System.err.println("Error: Type of partition " + i + " contains invalid characters."); return false; } } } /* Re-open file in write mode. */ try { options.file.close(); options.file = new RandomAccessFile(filename, "rw"); } catch(FileNotFoundException ex) { System.err.println("Error: Failed to open file \"" + filename + "\"."); return false; } catch(IOException ex) { ex.printStackTrace(); return false; } return true; } private static void printUsage() { System.err.println("usage: mkapm --sector-size <sector size> --part " + "<start offset> <length> <partition name> <partition type>"); } private static void writeWithPadding(RandomAccessFile file, byte[] data, int paddedSize) throws IOException { byte[] fullBlock = new byte[paddedSize]; System.arraycopy(data, 0, fullBlock, 0, data.length); Arrays.fill(fullBlock, data.length, fullBlock.length, (byte) 0); file.write(fullBlock); } public static void main(String[] args) { System.err.println("YO! args.length=" + args.length); if(args.length == 0 || !parseOptions(args)) { printUsage(); System.exit(1); return; } final long deviceByteCount; try { deviceByteCount = options.file.length(); } catch(IOException ioe) { ioe.printStackTrace(); System.exit(1); return; } if((deviceByteCount % options.blockSize) != 0) { System.err.println("Error: File size is not a multiple of sector " + "size."); printUsage(); System.exit(1); return; } final long deviceBlockCount = deviceByteCount / options.blockSize; DriverDescriptorRecord ddr = new DriverDescriptorRecord( options.blockSize, deviceBlockCount); LinkedList<APMPartition> partitions = new LinkedList<APMPartition>(); /* First partition always describes the partition map itself. */ partitions.add(new APMPartition(2, 1, 15, "Apple", "Apple_partition_map", 0x0, options.blockSize)); for(PartitionSpec ps : options.partitions) { APMPartition curPart = new APMPartition(2, ps.partitionByteStart / options.blockSize, ps.partitionByteLength / options.blockSize, ps.partitionName, ps.partitionType, 0x40000077, options.blockSize); partitions.add(curPart); } try { writeWithPadding(options.file, ddr.getData(), options.blockSize); for(APMPartition p : partitions) { System.err.println("Writing out partition:"); p.print(System.err, "\t"); writeWithPadding(options.file, p.getData(), options.blockSize); } writeWithPadding(options.file, new byte[0], 15*options.blockSize - (partitions.size()+1)*options.blockSize); options.file.close(); } catch(IOException e) { e.printStackTrace(); } } }