// Spludlow Software // Copyright © Samuel P. Ludlow 2020 All Rights Reserved // Distributed under the terms of the GNU General Public License version 3 // Distributed WITHOUT ANY WARRANTY; without implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE // https://www.spludlow.co.uk/LICENCE.TXT // The Spludlow logo is a registered trademark of Samuel P. Ludlow and may not be used without permission // v1.14 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.IO; namespace Spludlow.Email { /// /// Create 'MacBinary' files that can be extracted on a Macintosh to restore the data fork, File Type, and Creator Code /// Used with the 'AppleDouble' class, from files normally emailed from a Macintosh /// public class MacBinary { public MacBinary() { } public void Save(string filename, AppleDouble appleDouble) { using (BinaryWriter writer = new BinaryWriter(new FileStream(filename, FileMode.Create))) { byte tempByte; byte[] tempByteArray; short tempWord; int tempLong; //000 Byte old version number, must be kept at zero for compatibility tempByte = 0; writer.Write(tempByte); //001 Byte Length of filename (must be in the range 1-31) tempByte = (byte)appleDouble.FileName.Length; writer.Write(tempByte); //002 1 to 63 Bytes filename (only "length" bytes are significant). tempByteArray = new byte[63]; for (int index = 0; index < tempByteArray.Length && index < appleDouble.FileNameData.Length; ++index) tempByteArray[index] = appleDouble.FileNameData[index]; writer.Write(tempByteArray); //065 Long Word file type (normally expressed as four characters) writer.Write(appleDouble.FileType, 0, 4); //069 Long Word file creator (normally expressed as four characters) writer.Write(appleDouble.CreatorCode, 0, 4); //073 Byte original Finder flags //Bit 7 - isAlias. //Bit 6 - isInvisible. //Bit 5 - hasBundle. //Bit 4 - nameLocked. //Bit 3 - isStationery. //Bit 2 - hasCustomIcon. //Bit 1 - reserved. //Bit 0 - hasBeenInited. tempByte = 0; writer.Write(tempByte); //074 Byte zero fill, must be zero for compatibility tempByte = 0; writer.Write(tempByte); //075 Word file's vertical position within its window. tempWord = 0; writer.Write(tempWord); //077 Word file's horizontal position within its window. tempWord = 0; writer.Write(tempWord); //079 Word file's window or folder ID. tempWord = 0; writer.Write(tempWord); //081 Byte "Protected" flag (in low order bit). tempByte = 0; writer.Write(tempByte); //082 Byte zero fill, must be zero for compatibility tempByte = 0; writer.Write(tempByte); //083 Long Word Data Fork length (bytes, zero if no Data Fork). writer.Write(ReverseInt32(appleDouble.DataFork.Length)); //087 Long Word Resource Fork length (bytes, zero if no R.F.). writer.Write(ReverseInt32(appleDouble.ResourceFork.Length)); //091 Long Word File's creation date tempLong = 0; writer.Write(tempLong); //095 Long Word File's "last modified" date. tempLong = 0; writer.Write(tempLong); //099 Word length of Get Info comment to be sent after the resource fork (if implemented, see below). tempWord = 0; writer.Write(tempWord); //101 Byte Finder Flags, bits 0-7. (Bits 8-15 are already in byte 73) //Bit 7 - hasNoInits //Bit 6 - isShared //Bit 5 - requiresSwitchLaunch //Bit 4 - ColorReserved //Bits 1-3 - color //Bit 0 - isOnDesk tempByte = 0; writer.Write(tempByte); //*102 Long Word signature for indentification purposes (‘mBIN’) tempByteArray = new byte[4]; tempByteArray[0] = (byte)'m'; tempByteArray[1] = (byte)'B'; tempByteArray[2] = (byte)'I'; tempByteArray[3] = (byte)'N'; writer.Write(tempByteArray, 0, 4); //*106 Byte script of file name (from the fdScript field of an fxInfo record) tempByte = 0; writer.Write(tempByte); //*107 Byte extended Finder flags (from the fdXFlags field of an fxInfo record) tempByte = 0; writer.Write(tempByte); //108-115 Unused (must be zeroed by creators, must be ignored by readers) tempByteArray = new byte[8]; writer.Write(tempByteArray, 0, 8); //116 Long Word Length of total files when packed files are unpacked. As of the //writing of this document, this field has never been used. tempLong = 0; writer.Write(tempLong); //120 Word Length of a secondary header. If this is non-zero, skip this many bytes (rounded up to the next multiple of 128). This is for future expansion only, when sending files with MacBinary, this word should be zero. tempWord = 0; writer.Write(tempWord); //*122 Byte Version number of MacBinary III that the uploading program is written for (the version is 130 for MacBinary III) tempByte = 130; writer.Write(tempByte); //123 Byte Minimum MacBinary version needed to read this file (set this value at 129 for backwards compatibility with MacBinary II) tempByte = 129; writer.Write(tempByte); //124 Word CRC of previous 124 bytes tempWord = 0; writer.Write(tempWord); // Round it to 128 tempWord = 0; writer.Write(tempWord); // Data Fork if (appleDouble.DataFork.Length != 0) { writer.Write(appleDouble.DataFork); int pad = PadBytesNeeded(appleDouble.DataFork.Length); if (pad > 0) { tempByteArray = new byte[pad]; writer.Write(tempByteArray, 0, pad); } } // Resource fork if (appleDouble.ResourceFork.Length != 0) { writer.Write(appleDouble.ResourceFork); int pad = PadBytesNeeded(appleDouble.ResourceFork.Length); if (pad > 0) { tempByteArray = new byte[pad]; writer.Write(tempByteArray, 0, pad); } } } } private int PadBytesNeeded(int size) { int pad = 128 - (size % 128); if (pad == 128) pad = 0; return pad; } private static int ReverseInt32(int input) { byte[] temp = BitConverter.GetBytes(input); Array.Reverse(temp); return BitConverter.ToInt32(temp, 0); } } }