summaryrefslogtreecommitdiffstatshomepage
path: root/src/mess/tools/imgtool/modules/mac.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/mess/tools/imgtool/modules/mac.c')
-rw-r--r--src/mess/tools/imgtool/modules/mac.c6414
1 files changed, 6414 insertions, 0 deletions
diff --git a/src/mess/tools/imgtool/modules/mac.c b/src/mess/tools/imgtool/modules/mac.c
new file mode 100644
index 00000000000..c72746f18b0
--- /dev/null
+++ b/src/mess/tools/imgtool/modules/mac.c
@@ -0,0 +1,6414 @@
+/****************************************************************************
+
+ mac.c
+
+ Handlers for Classic MacOS images (MFS and HFS formats).
+
+ Raphael Nabet, 2003
+
+ TODO:
+ * add support for HFS write
+
+*****************************************************************************
+
+ terminology:
+ disk block: 512-byte logical block. With sectors of 512 bytes, one logical
+ block is equivalent to one sector; when the sector size is not 512
+ bytes, sectors are split or grouped to make 512-byte disk blocks.
+ allocation block: The File Manager always allocates logical disk blocks to
+ a file in groups called allocation blocks; an allocation block is
+ simply a group of consecutive logical blocks. The size of a volume's
+ allocation blocks depends on the capacity of the volume; there can be
+ at most 4094 (MFS) or 65535 (HFS) allocation blocks on a volume.
+ MFS (Macintosh File System): File system used by the early Macintosh. This
+ File system does not support folders (you may create folders on a MFS
+ disk, but such folders are not implemented on File System level but in
+ the Desktop file, and they are just a hint of how programs should list
+ files, i.e. you can't have two files with the same name on a volume
+ even if they are in two different folders), and it is not adequate for
+ large volumes.
+ HFS (Hierarchical File System): File system introduced with the HD20
+ harddisk, the Macintosh Plus ROMs, and system 3.2 (IIRC). Contrary to
+ MFS, it supports hierarchical folders. Also, it is suitable for larger
+ volumes.
+ HFS+ (HFS Plus): New file system introduced with MacOS 8.1. It has a lot
+ in common with HFS, but it supports more allocation blocks (up to 4
+ billions IIRC), and many extra features, including longer file names
+ (up to 255 UTF-16 Unicode chars).
+ tag data: with the GCR encoding, each disk block is associated with a 12
+ (3.5" floppies) or 20 (HD20) byte tag record. This tag record contains
+ information on the block allocation status (whether it is allocated
+ in a file or free, which file is it belongs to, what offset the block
+ has in the file). This enables to recover all files whose data blocks
+ are still on the disk surface even if the disk catalog has been trashed
+ completely (though most file properties, like the name, type and
+ logical EOF, are not saved in the tag record and cannot be recovered).
+
+ Organization of an MFS volume:
+
+ Logical Contents Allocation block
+ block
+
+ 0 - 1: System startup information
+ 2 - m: Master directory block (MDB)
+ + allocation block link pointers
+ m+1 - n: Directory file
+ n+1 - p-2: Other files and free space 0 - ((p-2)-(n+1))/k
+ p-1: Alternate MDB
+ p: Not used
+ usually, k = 2, m = 3, n = 16, p = 799 (SSDD 3.5" floppy)
+ with DSDD 3.5" floppy, I assume that p = 1599, but I don't know the other
+ values
+
+
+ Master Directory Block:
+
+ Offset Length Description
+ ------ ------ -----------
+ 0 2 Volume Signature
+ 2 4 Creation Date
+ 6 4 Last Modification Date
+ 10 2 Volume Attributes
+ 12 2 Number of Files In Root Directory
+ 14 2 First Block of Volume Bitmap
+ ...
+
+ Links:
+ http://developer.apple.com/documentation/mac/Files/Files-99.html
+ http://developer.apple.com/documentation/mac/Files/Files-100.html
+ http://developer.apple.com/documentation/mac/Files/Files-101.html
+ http://developer.apple.com/documentation/mac/Files/Files-102.html
+ http://developer.apple.com/documentation/mac/Files/Files-103.html
+ http://developer.apple.com/documentation/mac/Files/Files-104.html
+ http://developer.apple.com/documentation/mac/Files/Files-105.html
+ http://developer.apple.com/documentation/mac/Files/Files-106.html
+ http://developer.apple.com/documentation/mac/Devices/Devices-121.html#MARKER-2-169
+ http://developer.apple.com/documentation/mac/MoreToolbox/MoreToolbox-99.html
+
+*****************************************************************************/
+
+#include <ctype.h>
+#include <string.h>
+#include <time.h>
+#include <limits.h>
+#include <stddef.h>
+
+#include "formats/imageutl.h"
+#include "imgtool.h"
+#include "macutil.h"
+#include "iflopimg.h"
+#include "formats/ap_dsk35.h"
+
+/* if 1, check consistency of B-Tree (most of the checks will eventually be
+ suppressed when the image is opened as read-only and only enabled when
+ the image is opened as read/write) */
+#define BTREE_CHECKS 1
+/* if 1, check consistency of tag data when we are at risk of corrupting the
+ disk (file write and allocation) */
+#define TAG_CHECKS 1
+/* if 1, check consistency of tag data when reading files (not recommended
+ IMHO) */
+#define TAG_EXTRA_CHECKS 0
+
+#if 0
+#pragma mark MISCELLANEOUS UTILITIES
+#endif
+
+typedef struct UINT16BE
+{
+ UINT8 bytes[2];
+} UINT16BE;
+
+typedef struct UINT24BE
+{
+ UINT8 bytes[3];
+} UINT24BE;
+
+typedef struct UINT32BE
+{
+ UINT8 bytes[4];
+} UINT32BE;
+
+INLINE UINT16 get_UINT16BE(UINT16BE word)
+{
+ return (word.bytes[0] << 8) | word.bytes[1];
+}
+
+INLINE void set_UINT16BE(UINT16BE *word, UINT16 data)
+{
+ word->bytes[0] = (data >> 8) & 0xff;
+ word->bytes[1] = data & 0xff;
+}
+
+INLINE UINT32 get_UINT24BE(UINT24BE word)
+{
+ return (word.bytes[0] << 16) | (word.bytes[1] << 8) | word.bytes[2];
+}
+
+INLINE void set_UINT24BE(UINT24BE *word, UINT32 data)
+{
+ word->bytes[0] = (data >> 16) & 0xff;
+ word->bytes[1] = (data >> 8) & 0xff;
+ word->bytes[2] = data & 0xff;
+}
+
+INLINE UINT32 get_UINT32BE(UINT32BE word)
+{
+ return (word.bytes[0] << 24) | (word.bytes[1] << 16) | (word.bytes[2] << 8) | word.bytes[3];
+}
+
+INLINE void set_UINT32BE(UINT32BE *word, UINT32 data)
+{
+ word->bytes[0] = (data >> 24) & 0xff;
+ word->bytes[1] = (data >> 16) & 0xff;
+ word->bytes[2] = (data >> 8) & 0xff;
+ word->bytes[3] = data & 0xff;
+}
+
+/*
+ Macintosh string: first byte is length
+*/
+typedef UINT8 mac_str27[28];
+typedef UINT8 mac_str31[32];
+typedef UINT8 mac_str63[64];
+typedef UINT8 mac_str255[256];
+
+/*
+ Macintosh type/creator code: 4 char value
+*/
+typedef UINT32BE mac_type;
+
+/*
+ point record, with the y and x coordinates
+*/
+typedef struct mac_point
+{
+ UINT16BE v; /* actually signed */
+ UINT16BE h; /* actually signed */
+} mac_point;
+
+/*
+ rect record, with the corner coordinates
+*/
+typedef struct mac_rect
+{
+ UINT16BE top; /* actually signed */
+ UINT16BE left; /* actually signed */
+ UINT16BE bottom;/* actually signed */
+ UINT16BE right; /* actually signed */
+} mac_rect;
+
+/*
+ FInfo (Finder file info) record
+*/
+typedef struct mac_FInfo
+{
+ mac_type type; /* file type */
+ mac_type creator; /* file creator */
+ UINT16BE flags; /* Finder flags */
+ mac_point location; /* file's location in window */
+ /* If set to {0, 0}, and the inited flag is
+ clear, the Finder will place the item
+ automatically */
+ UINT16BE fldr; /* MFS: window that contains file */
+ /* -3: trash */
+ /* -2: desktop */
+ /* -1: new folder template?????? */
+ /* 0: disk window ("root") */
+ /* > 0: specific folder, index of FOBJ resource??? */
+ /* The FOBJ resource contains some folder info;
+ the name of the resource is the folder name. */
+ /* HFS & HFS+:
+ System 7: The window in which the file???s icon appears
+ System 8: reserved (set to 0) */
+} mac_FInfo;
+
+/*
+ FXInfo (Finder extended file info) record -- not found in MFS
+*/
+typedef struct mac_FXInfo
+{
+ UINT16BE iconID; /* System 7: An ID number for the file???s icon; the
+ numbers that identify icons are assigned by the
+ Finder */
+ /* System 8: Reserved (set to 0) */
+ UINT16BE reserved[3]; /* Reserved (set to 0) */
+ UINT8 script; /* System 7: if high-bit is set, the script code
+ for displaying the file name; ignored otherwise */
+ /* System 8: Extended flags MSB(?) */
+ UINT8 XFlags; /* Extended flags */
+ UINT16BE comment; /* System 7: Comment ID if high-bit is clear */
+ /* System 8: Reserved (set to 0) */
+ UINT32BE putAway; /* Put away folder ID (i.e. if the user moves the
+ file onto the desktop, the directory ID of the
+ folder from which the user moves the file is
+ saved here) */
+} mac_FXInfo;
+
+/*
+ DInfo (Finder folder info) record -- not found in MFS
+*/
+typedef struct mac_DInfo
+{
+ mac_rect rect; /* Folder's window bounds */
+ UINT16BE flags; /* Finder flags, e.g. kIsInvisible, kNameLocked, etc */
+ mac_point location; /* Location of the folder in parent window */
+ /* If set to {0, 0}, and the initied flag is
+ clear, the Finder will place the item
+ automatically */
+ UINT16BE view; /* System 7: The manner in which folders are
+ displayed */
+ /* System 8: reserved (set to 0) */
+} mac_DInfo;
+
+/*
+ DXInfo (Finder extended folder info) record -- not found in MFS
+*/
+typedef struct mac_DXInfo
+{
+ mac_point scroll; /* Scroll position */
+ UINT32BE openChain; /* System 7: chain of directory IDs for open folders */
+ /* System 8: reserved (set to 0) */
+ UINT8 script; /* System 7: if high-bit is set, the script code
+ for displaying the folder name; ignored otherwise */
+ /* System 8: Extended flags MSB(?) */
+ UINT8 XFlags; /* Extended flags */
+ UINT16BE comment; /* System 7: Comment ID if high-bit is clear */
+ /* System 8: Reserved (set to 0) */
+ UINT32BE putAway; /* Put away folder ID (i.e. if the user moves the
+ folder onto the desktop, the directory ID of
+ the folder from which the user moves it is
+ saved here) */
+} mac_DXInfo;
+
+/*
+ defines for FInfo & DInfo flags fields
+*/
+enum
+{
+ fif_isOnDesk = 0x0001, /* System 6: set if item is located on desktop (files and folders) */
+ /* System 7: Unused (set to 0) */
+ fif_color = 0x000E, /* color coding (files and folders) */
+ fif_colorReserved = 0x0010, /* System 6: reserved??? */
+ /* System 7: Unused (set to 0) */
+ fif_requiresSwitchLaunch= 0x0020, /* System 6: ??? */
+ /* System 7: Unused (set to 0) */
+ fif_isShared = 0x0040, /* Applications files: if set, the */
+ /* application can be executed by */
+ /* multiple users simultaneously. */
+ /* Otherwise, set to 0. */
+ fif_hasNoINITs = 0x0080, /* Extensions/Control Panels: if set(?), */
+ /* this file contains no INIT */
+ /* resource */
+ /* Otherwise, set to 0. */
+ fif_hasBeenInited = 0x0100, /* System 6: The Finder has recorded information from
+ the file???s bundle resource into the desktop
+ database and given the file or folder a
+ position on the desktop. */
+ /* System 7? 8?: Clear if the file contains desktop database */
+ /* resources ('BNDL', 'FREF', 'open', 'kind'...) */
+ /* that have not been added yet. Set only by the Finder */
+ /* Reserved for folders - make sure this bit is cleared for folders */
+
+ /* bit 0x0200 was at a point (AOCE for system 7.x?) the letter bit for
+ AOCE, but was reserved before and it is reserved again in recent MacOS
+ releases. */
+
+ fif_hasCustomIcon = 0x0400, /* (files and folders) */
+ fif_isStationery = 0x0800, /* System 7: (files only) */
+ fif_nameLocked = 0x1000, /* (files and folders) */
+ fif_hasBundle = 0x2000, /* Files only */
+ fif_isInvisible = 0x4000, /* (files and folders) */
+ fif_isAlias = 0x8000 /* System 7: (files only) */
+};
+
+/*
+ mac_to_c_strncpy()
+
+ Encode a macintosh string as a C string. The NUL character is escaped,
+ using the "%00" sequence. To avoid any ambiguity, '%' is escaped with
+ '%25'.
+
+ dst (O): C string
+ n (I): length of buffer pointed to by dst
+ src (I): macintosh string (first byte is length)
+*/
+static void mac_to_c_strncpy(char *dst, int n, UINT8 *src)
+{
+ size_t len = src[0];
+ int i, j;
+
+ i = j = 0;
+ while ((i < len) && (j < n-1))
+ {
+ switch (src[i+1])
+ {
+ case '\0':
+ if (j >= n-3)
+ goto exit;
+ dst[j] = '%';
+ dst[j+1] = '0';
+ dst[j+2] = '0';
+ j += 3;
+ break;
+
+ case '%':
+ if (j >= n-3)
+ goto exit;
+ dst[j] = '%';
+ dst[j+1] = '2';
+ dst[j+2] = '5';
+ j += 3;
+ break;
+
+ default:
+ dst[j] = src[i+1];
+ j++;
+ break;
+ }
+ i++;
+ }
+
+exit:
+ if (n > 0)
+ dst[j] = '\0';
+}
+
+/*
+ c_to_mac_strncpy()
+
+ Decode a C string to a macintosh string. The NUL character is escaped,
+ using the "%00" sequence. To avoid any ambiguity, '%' is escaped with
+ '%25'.
+
+ dst (O): macintosh string (first byte is length)
+ src (I): C string
+ n (I): length of string pointed to by src
+*/
+static void c_to_mac_strncpy(mac_str255 dst, const char *src, int n)
+{
+ size_t len;
+ int i;
+ char buf[3];
+
+
+ len = 0;
+ i = 0;
+ while ((i < n) && (len < 255))
+ {
+ switch (src[i])
+ {
+ case '%':
+ if (i >= n-2)
+ goto exit;
+ buf[0] = src[i+1];
+ buf[1] = src[i+2];
+ buf[2] = '\0';
+ dst[len+1] = strtoul(buf, NULL, 16);
+ i += 3;
+ break;
+
+ default:
+ dst[len+1] = src[i];
+ i++;
+ break;
+ }
+ len++;
+ }
+
+exit:
+ dst[0] = len;
+}
+
+/*
+ mac_strcmp()
+
+ Compare two macintosh strings
+
+ s1 (I): the string to compare
+ s2 (I): the comparison string
+
+ Return a zero if s1 and s2 are equal, a negative value if s1 is less than
+ s2, and a positive value if s1 is greater than s2.
+*/
+#ifdef UNUSED_FUNCTION
+static int mac_strcmp(const UINT8 *s1, const UINT8 *s2)
+{
+ size_t common_len;
+
+ common_len = (s1[0] <= s2[0]) ? s1[0] : s2[0];
+
+ return memcmp(s1+1, s2+1, common_len) || ((int)s1[0] - s2[0]);
+}
+#endif
+
+/*
+ mac_stricmp()
+
+ Compare two macintosh strings in a manner compatible with the macintosh HFS
+ file system.
+
+ This functions emulates the way HFS (and MFS???) sorts string: this is
+ equivalent to the RelString macintosh toolbox call with the caseSensitive
+ parameter as false and the diacSensitive parameter as true.
+
+ s1 (I): the string to compare
+ s2 (I): the comparison string
+
+ Return a zero if s1 and s2 are equal, a negative value if s1 is less than
+ s2, and a positive value if s1 is greater than s2.
+
+Known issues:
+ Using this function makes sense with the MacRoman encoding, as it means the
+ computer will regard "DeskTop File", "Desktop File", "Desktop file", etc,
+ as the same file. (UNIX users will probably regard this as an heresy, but
+ you must consider that, unlike UNIX, the Macintosh was not designed for
+ droids, but error-prone human beings that may forget about case.)
+
+ (Also, letters with diatrical signs follow the corresponding letters
+ without diacritical signs in the HFS catalog file. This does not matter,
+ though, since the Finder will not display files in the HFS catalog order,
+ but it will instead sort files according to whatever order is currently
+ selected in the View menu.)
+
+ However, with other text encodings, the behavior will be completely absurd.
+ For instance, with the Greek encoding, it will think that the degree symbol
+ is the same letter (with different case) as the upper-case Psi, so that if
+ a program asks for a file called "90??" on a greek HFS volume, and there is
+ a file called "90??" on this volume, file "90??" will be opened.
+ Results will probably be even weirder with 2-byte scripts like Japanese or
+ Chinese. Of course, we are not going to fix this issue, since doing so
+ would break the compatibility with the original Macintosh OS.
+*/
+
+static int mac_stricmp(const UINT8 *s1, const UINT8 *s2)
+{
+ static const unsigned char mac_char_sort_table[256] =
+ {
+ /* \x00 \x01 \x02 \x03 \x04 \x05 \x06 \x07 */
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ /* \x08 \x09 \x0a \x0b \x0c \x0d \x0e \x0f */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ /* \x10 \x11 \x12 \x13 \x14 \x15 \x16 \x17 */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ /* \x18 \x19 \x1a \x1b \x1c \x1d \x1e \x1f */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ /* \x20 \x21 \x22 \x23 \x24 \x25 \x26 \x27 */
+ 0x20, 0x21, 0x22, 0x27, 0x28, 0x29, 0x2a, 0x2b,
+ /* \x28 \x29 \x2a \x2b \x2c \x2d \x2e \x2f */
+ 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35,
+ /* \x30 \x31 \x32 \x33 \x34 \x35 \x36 \x37 */
+ 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d,
+ /* \x38 \x39 \x3a \x3b \x3c \x3d \x3e \x3f */
+ 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45,
+ /* \x40 \x41 \x42 \x43 \x44 \x45 \x46 \x47 */
+ 0x46, 0x47, 0x51, 0x52, 0x54, 0x55, 0x5a, 0x5b,
+ /* \x48 \x49 \x4a \x4b \x4c \x4d \x4e \x4f */
+ 0x5c, 0x5d, 0x62, 0x63, 0x64, 0x65, 0x66, 0x68,
+ /* \x50 \x51 \x52 \x53 \x54 \x55 \x56 \x57 */
+ 0x71, 0x72, 0x73, 0x74, 0x76, 0x77, 0x7c, 0x7d,
+ /* \x58 \x59 \x5a \x5b \x5c \x5d \x5e \x5f */
+ 0x7e, 0x7f, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86,
+ /* \x60 \x61 \x62 \x63 \x64 \x65 \x66 \x67 */
+ 0x4d, 0x47, 0x51, 0x52, 0x54, 0x55, 0x5a, 0x5b,
+ /* \x68 \x69 \x6a \x6b \x6c \x6d \x6e \x6f */
+ 0x5c, 0x5d, 0x62, 0x63, 0x64, 0x65, 0x66, 0x68,
+ /* \x70 \x71 \x72 \x73 \x74 \x75 \x76 \x77 */
+ 0x71, 0x72, 0x73, 0x74, 0x76, 0x77, 0x7c, 0x7d,
+ /* \x78 \x79 \x7a \x7b \x7c \x7d \x7e \x7f */
+ 0x7e, 0x7f, 0x81, 0x87, 0x88, 0x89, 0x8a, 0x8b,
+ /* \x80 \x81 \x82 \x83 \x84 \x85 \x86 \x87 */
+ 0x49, 0x4b, 0x53, 0x56, 0x67, 0x69, 0x78, 0x4e,
+ /* \x88 \x89 \x8a \x8b \x8c \x8d \x8e \x8f */
+ 0x48, 0x4f, 0x49, 0x4a, 0x4b, 0x53, 0x56, 0x57,
+ /* \x90 \x91 \x92 \x93 \x94 \x95 \x96 \x97 */
+ 0x58, 0x59, 0x5e, 0x5f, 0x60, 0x61, 0x67, 0x6d,
+ /* \x98 \x99 \x9a \x9b \x9c \x9d \x9e \x9f */
+ 0x6e, 0x6f, 0x69, 0x6a, 0x79, 0x7a, 0x7b, 0x78,
+ /* \xa0 \xa1 \xa2 \xa3 \xa4 \xa5 \xa6 \xa7 */
+ 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x75,
+ /* \xa8 \xa9 \xaa \xab \xac \xad \xae \xaf */
+ 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x4c, 0x6b,
+ /* \xb0 \xb1 \xb2 \xb3 \xb4 \xb5 \xb6 \xb7 */
+ 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0,
+ /* \xb8 \xb9 \xba \xbb \xbc \xbd \xbe \xbf */
+ 0xa1, 0xa2, 0xa3, 0x50, 0x70, 0xa4, 0x4c, 0x6b,
+ /* \xc0 \xc1 \xc2 \xc3 \xc4 \xc5 \xc6 \xc7 */
+ 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0x25,
+ /* \xc8 \xc9 \xca \xcb \xcc \xcd \xce \xcf */
+ 0x26, 0xac, 0x20, 0x48, 0x4a, 0x6a, 0x6c, 0x6c,
+ /* \xd0 \xd1 \xd2 \xd3 \xd4 \xd5 \xd6 \xd7 */
+ 0xad, 0xae, 0x23, 0x24, 0x2c, 0x2d, 0xaf, 0xb0,
+ /* \xd8 \xd9 \xda \xdb \xdc \xdd \xde \xdf */
+ 0x80, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
+ /* \xe0 \xe1 \xe2 \xe3 \xe4 \xe5 \xe6 \xe7 */
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
+ /* \xe8 \xe9 \xea \xeb \xec \xed \xee \xef */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
+ /* \xf0 \xf1 \xf2 \xf3 \xf4 \xf5 \xf6 \xf7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
+ /* \xf8 \xf9 \xfa \xfb \xfc \xfd \xfe \xff */
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7
+ };
+
+ size_t common_len;
+ int i;
+ int c1, c2;
+
+ common_len = (s1[0] <= s2[0]) ? s1[0] : s2[0];
+
+ for (i=0; i<common_len; i++)
+ {
+ c1 = mac_char_sort_table[s1[i+1]];
+ c2 = mac_char_sort_table[s2[i+1]];
+ if (c1 != c2)
+ return c1 - c2;
+ }
+
+ return ((int)s1[0] - s2[0]);
+}
+
+/*
+ mac_strcpy()
+
+ Copy a macintosh string
+
+ dst (O): dest macintosh string
+ src (I): source macintosh string (first byte is length)
+
+ Return a zero if s1 and s2 are equal, a negative value if s1 is less than
+ s2, and a positive value if s1 is greater than s2.
+*/
+INLINE void mac_strcpy(UINT8 *dest, const UINT8 *src)
+{
+ memcpy(dest, src, src[0]+1);
+}
+
+#ifdef UNUSED_FUNCTION
+/*
+ mac_strncpy()
+
+ Copy a macintosh string
+
+ dst (O): dest macintosh string
+ n (I): max string length for dest (range 0-255, buffer length + 1)
+ src (I): source macintosh string (first byte is length)
+*/
+static void mac_strncpy(UINT8 *dest, int n, const UINT8 *src)
+{
+ size_t len;
+
+ len = src[0];
+ if (len > n)
+ len = n;
+ dest[0] = len;
+ memcpy(dest+1, src+1, len);
+}
+#endif
+
+/*
+ disk image reference
+*/
+typedef struct mac_l1_imgref
+{
+ imgtool_image *image;
+ UINT32 heads;
+} mac_l1_imgref;
+
+
+
+static imgtoolerr_t mac_find_block(mac_l1_imgref *image, int block,
+ UINT32 *track, UINT32 *head, UINT32 *sector)
+{
+ *track = 0;
+ while(block >= (apple35_sectors_per_track(imgtool_floppy(image->image), *track) * image->heads))
+ {
+ block -= (apple35_sectors_per_track(imgtool_floppy(image->image), (*track)++) * image->heads);
+ if (*track >= 80)
+ return IMGTOOLERR_SEEKERROR;
+ }
+
+ *head = block / apple35_sectors_per_track(imgtool_floppy(image->image), *track);
+ *sector = block % apple35_sectors_per_track(imgtool_floppy(image->image), *track);
+ return IMGTOOLERR_SUCCESS;
+}
+
+
+
+/*
+ image_read_block()
+
+ Read one 512-byte block of data from a macintosh disk image
+
+ image (I/O): level-1 image reference
+ block (I): address of block to read
+ dest (O): buffer where block data should be stored
+
+ Return imgtool error code
+*/
+static imgtoolerr_t image_read_block(mac_l1_imgref *image, UINT32 block, void *dest)
+{
+ imgtoolerr_t err;
+ floperr_t ferr;
+ UINT32 track, head, sector;
+
+ err = mac_find_block(image, block, &track, &head, &sector);
+ if (err)
+ return err;
+
+ ferr = floppy_read_sector(imgtool_floppy(image->image), head, track, sector, 0, dest, 512);
+ if (ferr)
+ return imgtool_floppy_error(ferr);
+
+ return IMGTOOLERR_SUCCESS;
+}
+
+/*
+ image_write_block()
+
+ Read one 512-byte block of data from a macintosh disk image
+
+ image (I/O): level-1 image reference
+ block (I): address of block to write
+ src (I): buffer with the block data
+
+ Return imgtool error code
+*/
+static imgtoolerr_t image_write_block(mac_l1_imgref *image, UINT32 block, const void *src)
+{
+ imgtoolerr_t err;
+ floperr_t ferr;
+ UINT32 track, head, sector;
+
+ err = mac_find_block(image, block, &track, &head, &sector);
+ if (err)
+ return err;
+
+ ferr = floppy_write_sector(imgtool_floppy(image->image), head, track, sector, 0, src, 512, 0); /* TODO: pass ddam argument from imgtool */
+ if (ferr)
+ return imgtool_floppy_error(ferr);
+
+ return IMGTOOLERR_SUCCESS;
+}
+
+/*
+ image_get_tag_len()
+
+ Get length of tag data (12 for GCR floppies, 20 for HD20, 0 otherwise)
+
+ image (I/O): level-1 image reference
+
+ Return tag length
+*/
+INLINE int image_get_tag_len(mac_l1_imgref *image)
+{
+ return 0;
+}
+
+
+
+/*
+ image_read_tag()
+
+ Read a 12- or 20-byte tag record associated with a disk block
+
+ image (I/O): level-1 image reference
+ block (I): address of block to read
+ dest (O): buffer where tag data should be stored
+
+ Return imgtool error code
+*/
+static imgtoolerr_t image_read_tag(mac_l1_imgref *image, UINT32 block, void *dest)
+{
+ return IMGTOOLERR_UNEXPECTED;
+}
+
+/*
+ image_write_tag()
+
+ Write a 12- or 20-byte tag record associated with a disk block
+
+ image (I/O): level-1 image reference
+ block (I): address of block to write
+ src (I): buffer with the tag data
+
+ Return imgtool error code
+*/
+static imgtoolerr_t image_write_tag(mac_l1_imgref *image, UINT32 block, const void *src)
+{
+ return IMGTOOLERR_UNEXPECTED;
+}
+
+#if 0
+#pragma mark -
+#pragma mark MFS/HFS WRAPPERS
+#endif
+
+typedef enum
+{
+ L2I_MFS,
+ L2I_HFS
+} mac_format;
+
+typedef enum { data_fork = 0x00, rsrc_fork = 0xff } mac_forkID;
+
+typedef struct mac_l2_imgref mac_l2_imgref;
+
+/*
+ MFS image ref
+*/
+typedef struct mfs_l2_imgref
+{
+ UINT16 dir_num_files;
+ UINT16 dir_start;
+ UINT16 dir_blk_len;
+
+ UINT16 ABStart;
+
+ mac_str27 volname;
+
+ unsigned char ABlink_dirty[13]; /* dirty flag for each disk block in the ABlink array */
+ UINT8 ABlink[6141];
+} mfs_l2_imgref;
+
+/*
+ HFS extent descriptor
+*/
+typedef struct hfs_extent
+{
+ UINT16BE stABN; /* first allocation block */
+ UINT16BE numABlks; /* number of allocation blocks */
+} hfs_extent;
+
+/*
+ HFS likes to group extents by 3 (it is 8 with HFS+), so we create a
+ specific type.
+*/
+typedef hfs_extent hfs_extent_3[3];
+
+/*
+ MFS open file ref
+*/
+typedef struct mfs_fileref
+{
+ UINT16 stBlk; /* first allocation block of file */
+} mfs_fileref;
+
+/*
+ HFS open file ref
+*/
+typedef struct hfs_fileref
+{
+ hfs_extent_3 extents; /* first 3 file extents */
+
+ UINT32 parID; /* CNID of parent directory (undefined for extent & catalog files) */
+ mac_str31 filename; /* file name (undefined for extent & catalog files) */
+} hfs_fileref;
+
+/*
+ MFS/HFS open file ref
+*/
+typedef struct mac_fileref
+{
+ mac_l2_imgref *l2_img; /* image pointer */
+
+ UINT32 fileID; /* file ID (a.k.a. CNID in HFS/HFS+) */
+
+ mac_forkID forkType; /* 0x00 for data, 0xff for resource */
+
+ UINT32 eof; /* logical end-of-file */
+ UINT32 pLen; /* physical end-of-file */
+
+ UINT32 crPs; /* current position in file */
+
+ UINT8 reload_buf;
+ UINT8 block_buffer[512]; /* buffer with current file block */
+
+ union
+ {
+ mfs_fileref mfs;
+ hfs_fileref hfs;
+ } u;
+} mac_fileref;
+
+/*
+ open BT ref
+*/
+typedef struct mac_BTref
+{
+ mac_fileref fileref; /* open B-tree file ref */
+
+ UINT16 nodeSize; /* size of a node, in bytes */
+ UINT32 rootNode; /* node number of root node */
+ UINT32 firstLeafNode; /* node number of first leaf node */
+ UINT32 attributes; /* persistent attributes about the tree */
+ UINT16 treeDepth; /* maximum height (usually leaf nodes) */
+ UINT16 maxKeyLength; /* maximum key length */
+
+ /* function to compare keys during tree searches */
+ int (*key_compare_func)(const void *key1, const void *key2);
+
+ void *node_buf; /* current node buffer */
+} mac_BTref;
+
+/*
+ Constants for BTHeaderRec attributes field
+*/
+enum
+{
+ btha_badCloseMask = 0x00000001, /* reserved */
+ btha_bigKeysMask = 0x00000002, /* key length field is 16 bits */
+ btha_variableIndexKeysMask = 0x00000004 /* keys in index nodes are variable length */
+};
+
+/*
+ HFS image ref
+*/
+typedef struct hfs_l2_imgref
+{
+ UINT16 VBM_start;
+
+ UINT16 ABStart;
+
+ mac_str27 volname;
+
+ mac_BTref extents_BT;
+ mac_BTref cat_BT;
+
+ UINT8 VBM[8192];
+} hfs_l2_imgref;
+
+/*
+ MFS/HFS image ref
+*/
+struct mac_l2_imgref
+{
+ mac_l1_imgref l1_img;
+
+ UINT16 numABs;
+ UINT16 blocksperAB;
+
+ UINT16 freeABs;
+
+ UINT32 nxtCNID; /* nxtFNum in MFS, nxtCNID in HFS */
+
+ mac_format format;
+ union
+ {
+ mfs_l2_imgref mfs;
+ hfs_l2_imgref hfs;
+ } u;
+};
+
+/*
+ MFS Master Directory Block
+*/
+typedef struct mfs_mdb_t
+{
+ UINT8 sigWord[2]; /* volume signature - always $D2D7 */
+ UINT32BE crDate; /* date and time of volume creation */
+ UINT32BE lsMod/*lsBkUp???*/;/* date and time of last modification (backup???) */
+ UINT16BE atrb; /* volume attributes (0x0000) */
+ /* bit 15 is set if volume is locked by software */
+ UINT16BE nmFls; /* number of files in directory */
+ UINT16BE dirSt; /* first block of directory */
+
+ UINT16BE blLn; /* length of directory in blocks (0x000C) */
+ UINT16BE nmAlBlks; /* number of allocation blocks in volume (0x0187) */
+ UINT32BE alBlkSiz; /* size (in bytes) of allocation blocks (0x00000400) */
+ UINT32BE clpSiz; /* default clump size - number of bytes to allocate
+ when a file grows (0x00002000) */
+ UINT16BE alBlSt; /* first allocation block in volume (0x0010) */
+
+ UINT32BE nxtFNum; /* next unused file number */
+ UINT16BE freeABs; /* number of unused allocation blocks */
+
+ mac_str27 VN; /* volume name */
+
+ UINT8 ABlink[512-64];/* Link array for file ABs. Array of nmAlBlks
+ 12-bit-long entries, indexed by AB address. If an
+ AB belongs to no file, the entry is 0; if an AB is
+ the last in any file, the entry is 1; if an AB
+ belongs to a file and is not the last one, the
+ entry is the AB address of the next file AB plus 1.
+ Note that the array extends on as many consecutive
+ disk blocks as needed (usually the MDB block plus
+ the next one). Incidentally, this array is not
+ saved in the secondary MDB: presumably, the idea
+ was that the disk utility could rely on the tag
+ data to rebuild the link array if it should ever
+ be corrupted. */
+} mfs_mdb_;
+
+/*
+ HFS Master Directory Block
+*/
+typedef struct hfs_mdb_t
+{
+/* First fields are similar to MFS, though several fields have a different meaning */
+ UINT8 sigWord[2]; /* volume signature - always $D2D7 */
+ UINT32BE crDate; /* date and time of volume creation */
+ UINT32BE lsMod; /* date and time of last modification */
+ UINT16BE atrb; /* volume attributes (0x0000) */
+ /* bit 15 is set if volume is locked by software */
+ UINT16BE nmFls; /* number of files in root folder */
+ UINT16BE VBMSt; /* first block of volume bitmap */
+ UINT16BE allocPtr; /* start of next allocation search */
+
+ UINT16BE nmAlBlks; /* number of allocation blocks in volume */
+ UINT32BE alBlkSiz; /* size (in bytes) of allocation blocks */
+ UINT32BE clpSiz; /* default clump size - number of bytes to allocate
+ when a file grows */
+ UINT16BE alBlSt; /* first allocation block in volume (0x0010) */
+ UINT32BE nxtCNID; /* next unused catalog node ID */
+ UINT16BE freeABs; /* number of unused allocation blocks */
+ mac_str27 VN; /* volume name */
+
+/* next fields are HFS-specific */
+
+ UINT32BE volBkUp; /* date and time of last backup */
+ UINT16BE vSeqNum; /* volume backup sequence number */
+ UINT32BE wrCnt; /* volume write count */
+ UINT32BE xtClpSiz; /* clump size for extents overflow file */
+ UINT32BE ctClpSiz; /* clump size for catalog file */
+ UINT16BE nmRtDirs; /* number of directories in root folder */
+ UINT32BE filCnt; /* number of files in volume */
+ UINT32BE dirCnt; /* number of directories in volume */
+ UINT8 fndrInfo[32]; /* information used by the Finder */
+
+ union
+ {
+ struct
+ {
+ UINT16BE VCSize; /* size (in blocks) of volume cache */
+ UINT16BE VBMCSize; /* size (in blocks) of volume bitmap cache */
+ UINT16BE ctlCSize; /* size (in blocks) of common volume cache */
+ } v1;
+ struct
+ {
+ UINT16BE embedSigWord; /* embedded volume signature */
+ hfs_extent embedExtent; /* embedded volume location and size */
+ } v2;
+ } u;
+
+ UINT32BE xtFlSize; /* size (in bytes) of extents overflow file */
+ hfs_extent_3 xtExtRec; /* extent record for extents overflow file */
+ UINT32BE ctFlSize; /* size (in bytes) of catalog file */
+ hfs_extent_3 ctExtRec; /* extent record for catalog file */
+} hfs_mdb_t;
+
+/* to save a little stack space, we use the same buffer for MDB and next blocks */
+typedef union img_open_buf
+{
+ mfs_mdb_t mfs_mdb;
+ hfs_mdb_t hfs_mdb;
+ UINT8 raw[512];
+} img_open_buf;
+
+/*
+ Information extracted from catalog/directory
+*/
+typedef struct mac_dirent
+{
+ UINT16 dataRecType; /* type of data record */
+
+ mac_FInfo flFinderInfo; /* information used by the Finder */
+ mac_FXInfo flXFinderInfo; /* information used by the Finder */
+
+ UINT8 flags; /* bit 0=1 if file locked */
+
+ UINT32 fileID; /* file ID in directory/catalog */
+
+ UINT32 dataLogicalSize; /* logical EOF of data fork */
+ UINT32 dataPhysicalSize; /* physical EOF of data fork */
+ UINT32 rsrcLogicalSize; /* logical EOF of resource fork */
+ UINT32 rsrcPhysicalSize; /* physical EOF of resource fork */
+
+ UINT32 createDate; /* date and time of creation */
+ UINT32 modifyDate; /* date and time of last modification */
+} mac_dirent;
+
+/*
+ Tag record for GCR floppies (12 bytes)
+
+ And, no, I don't know the format of the 20-byte tag record of the HD20
+*/
+typedef struct floppy_tag_record
+{
+ UINT32BE fileID; /* a.k.a. CNID */
+ /* a value of 1 seems to be the default for non-AB blocks, but this is not consistent */
+ UINT8 ftype; /* bit 1 = 1 if resource fork */
+ /* bit 0 = 1 if block is allocated to user file (i.e. it is not
+ in HFS extent & catalog, and not in non-AB blocks such
+ as MDB and MFS directory)??? */
+ /* bit 7 seems to be used, but I don't know what it means */
+ /* a value of $FF seems to be the default for non-AB blocks, but this is not consistent */
+ UINT8 fattr; /* bit 0 = 1 if locked(?) */
+ /* a value of $FF seems to be the default for non-AB blocks, but this is not consistent */
+ UINT16BE fblock; /* relative file block number (enough for any volume up to 32 MBytes in size) */
+ UINT32BE wrCnt; /* MFS: date and time of last write */
+ /* HFS: seems related to the wrCnt field in the mdb, i.e.
+ each time a volume is written to, the current value of
+ wrCnt is written in the tag field, then it is incremented */
+ /* (DV17 says "disk block number", but it cannot be true) */
+} floppy_tag_record;
+
+#ifdef UNUSED_FUNCTION
+static void hfs_image_close(mac_l2_imgref *l2_img);
+#endif
+static imgtoolerr_t mfs_file_get_nth_block_address(mac_fileref *fileref, UINT32 block_num, UINT32 *block_address);
+static imgtoolerr_t hfs_file_get_nth_block_address(mac_fileref *fileref, UINT32 block_num, UINT32 *block_address);
+static imgtoolerr_t mfs_lookup_path(mac_l2_imgref *l2_img, const char *fpath, mac_str255 filename, mac_dirent *cat_info, int create_it);
+static imgtoolerr_t hfs_lookup_path(mac_l2_imgref *l2_img, const char *fpath, UINT32 *parID, mac_str255 filename, mac_dirent *cat_info);
+static imgtoolerr_t mfs_file_open(mac_l2_imgref *l2_img, const mac_str255 filename, mac_forkID fork, mac_fileref *fileref);
+static imgtoolerr_t hfs_file_open(mac_l2_imgref *l2_img, UINT32 parID, const mac_str255 filename, mac_forkID fork, mac_fileref *fileref);
+static imgtoolerr_t mfs_file_setABeof(mac_fileref *fileref, UINT32 newABeof);
+static imgtoolerr_t mfs_dir_update(mac_fileref *fileref);
+
+static mac_l2_imgref *get_imgref(imgtool_image *img)
+{
+ return (mac_l2_imgref *) imgtool_floppy_extrabytes(img);
+}
+
+
+#ifdef UNUSED_FUNCTION
+/*
+ mac_image_close
+
+ Close a macintosh image.
+
+ l2_img (I/O): level-2 image reference
+*/
+static void mac_image_close(mac_l2_imgref *l2_img)
+{
+ switch (l2_img->format)
+ {
+ case L2I_MFS:
+ break;
+
+ case L2I_HFS:
+ hfs_image_close(l2_img);
+ break;
+ }
+}
+#endif
+
+/*
+ mac_lookup_path
+
+ Resolve a file path, and translate it to a parID + filename pair that enables
+ to do an efficient file search on a HFS volume (and an inefficient one on
+ MFS, but it is not an issue as MFS volumes typically have a few dozens
+ files, vs. possibly thousands with HFS volumes).
+
+ l2_img (I/O): level-2 image reference
+ fpath (I): file path (C string)
+ parID (O): set to the CNID of the parent directory if the volume is in HFS
+ format (reserved for MFS volumes)
+ filename (O): set to the actual name of the file, with capitalization matching
+ the one on the volume rather than the one in the fpath parameter (Mac
+ string)
+ cat_info (O): catalog info for this file extracted from the catalog file
+ (may be NULL)
+
+ Return imgtool error code
+*/
+static imgtoolerr_t mac_lookup_path(mac_l2_imgref *l2_img, const char *fpath, UINT32 *parID, mac_str255 filename, mac_dirent *cat_info, int create_it)
+{
+ imgtoolerr_t err = IMGTOOLERR_UNEXPECTED;
+
+ switch (l2_img->format)
+ {
+ case L2I_MFS:
+ *parID = 0;
+ err = mfs_lookup_path(l2_img, fpath, filename, cat_info, create_it);
+ break;
+
+ case L2I_HFS:
+ err = hfs_lookup_path(l2_img, fpath, parID, filename, cat_info);
+ break;
+ }
+ return err;
+}
+
+/*
+ mac_file_open
+
+ Open a file located on a macintosh image
+
+ l2_img (I/O): level-2 image reference
+ parID (I): CNID of the parent directory if the volume is in HFS format
+ (reserved for MFS volumes)
+ filename (I): name of the file (Mac string)
+ mac_forkID (I): tells which fork should be opened
+ fileref (O): mac file reference to open
+
+ Return imgtool error code
+*/
+static imgtoolerr_t mac_file_open(mac_l2_imgref *l2_img, UINT32 parID, const mac_str255 filename, mac_forkID fork, mac_fileref *fileref)
+{
+ switch (l2_img->format)
+ {
+ case L2I_MFS:
+ return mfs_file_open(l2_img, filename, fork, fileref);
+
+ case L2I_HFS:
+ return hfs_file_open(l2_img, parID, filename, fork, fileref);
+ }
+
+ return IMGTOOLERR_UNEXPECTED;
+}
+
+/*
+ mac_file_read
+
+ Read data from an open mac file, starting at current position in file
+
+ fileref (I/O): mac file reference
+ len (I): number of bytes to read
+ dest (O): destination buffer
+
+ Return imgtool error code
+*/
+static imgtoolerr_t mac_file_read(mac_fileref *fileref, UINT32 len, void *dest)
+{
+ UINT32 block = 0;
+ floppy_tag_record tag;
+ int run_len;
+ imgtoolerr_t err = IMGTOOLERR_SUCCESS;
+
+ if ((fileref->crPs + len) > fileref->eof)
+ /* EOF */
+ return IMGTOOLERR_UNEXPECTED;
+
+ while (len > 0)
+ {
+ if (fileref->reload_buf)
+ {
+ switch (fileref->l2_img->format)
+ {
+ case L2I_MFS:
+ err = mfs_file_get_nth_block_address(fileref, fileref->crPs/512, &block);
+ break;
+
+ case L2I_HFS:
+ err = hfs_file_get_nth_block_address(fileref, fileref->crPs/512, &block);
+ break;
+ }
+ if (err)
+ return err;
+ err = image_read_block(&fileref->l2_img->l1_img, block, fileref->block_buffer);
+ if (err)
+ return err;
+ fileref->reload_buf = FALSE;
+
+ if (TAG_EXTRA_CHECKS)
+ {
+ /* optional check */
+ if (image_get_tag_len(&fileref->l2_img->l1_img) == 12)
+ {
+ err = image_read_tag(&fileref->l2_img->l1_img, block, &tag);
+ if (err)
+ return err;
+
+ if ((get_UINT32BE(tag.fileID) != fileref->fileID)
+ || (((tag.ftype & 2) != 0) != (fileref->forkType == rsrc_fork))
+ || (get_UINT16BE(tag.fblock) != ((fileref->crPs/512) & 0xffff)))
+ {
+ return IMGTOOLERR_CORRUPTIMAGE;
+ }
+ }
+ }
+ }
+ run_len = 512 - (fileref->crPs % 512);
+ if (run_len > len)
+ run_len = len;
+
+ memcpy(dest, fileref->block_buffer+(fileref->crPs % 512), run_len);
+ len -= run_len;
+ dest = (UINT8 *)dest + run_len;
+ fileref->crPs += run_len;
+ if ((fileref->crPs % 512) == 0)
+ fileref->reload_buf = TRUE;
+ }
+
+ return IMGTOOLERR_SUCCESS;
+}
+
+/*
+ mac_file_write
+
+ Write data to an open mac file, starting at current position in file
+
+ fileref (I/O): mac file reference
+ len (I): number of bytes to read
+ dest (O): destination buffer
+
+ Return imgtool error code
+*/
+static imgtoolerr_t mac_file_write(mac_fileref *fileref, UINT32 len, const void *src)
+{
+ UINT32 block = 0;
+ floppy_tag_record tag;
+ int run_len;
+ imgtoolerr_t err = IMGTOOLERR_SUCCESS;
+
+ if ((fileref->crPs + len) > fileref->eof)
+ /* EOF */
+ return IMGTOOLERR_UNEXPECTED;
+
+ while (len > 0)
+ {
+ switch (fileref->l2_img->format)
+ {
+ case L2I_MFS:
+ err = mfs_file_get_nth_block_address(fileref, fileref->crPs/512, &block);
+ break;
+
+ case L2I_HFS:
+ err = hfs_file_get_nth_block_address(fileref, fileref->crPs/512, &block);
+ break;
+ }
+ if (err)
+ return err;
+
+ if (TAG_CHECKS)
+ {
+ /* optional check */
+ if (image_get_tag_len(&fileref->l2_img->l1_img) == 12)
+ {
+ err = image_read_tag(&fileref->l2_img->l1_img, block, &tag);
+ if (err)
+ return err;
+
+ if ((get_UINT32BE(tag.fileID) != fileref->fileID)
+ || (((tag.ftype & 2) != 0) != (fileref->forkType == rsrc_fork))
+ || (get_UINT16BE(tag.fblock) != ((fileref->crPs/512) & 0xffff)))
+ {
+ return IMGTOOLERR_CORRUPTIMAGE;
+ }
+ }
+ }
+
+ if (fileref->reload_buf)
+ {
+ err = image_read_block(&fileref->l2_img->l1_img, block, fileref->block_buffer);
+ if (err)
+ return err;
+ fileref->reload_buf = FALSE;
+ }
+ run_len = 512 - (fileref->crPs % 512);
+ if (run_len > len)
+ run_len = len;
+
+ memcpy(fileref->block_buffer+(fileref->crPs % 512), src, run_len);
+ err = image_write_block(&fileref->l2_img->l1_img, block, fileref->block_buffer);
+ if (err)
+ return err;
+ /* update tag data */
+ if (image_get_tag_len(&fileref->l2_img->l1_img) == 12)
+ {
+ if (!TAG_CHECKS)
+ {
+ err = image_read_tag(&fileref->l2_img->l1_img, block, &tag);
+ if (err)
+ return err;
+ }
+
+ switch (fileref->l2_img->format)
+ {
+ case L2I_MFS:
+ set_UINT32BE(&tag.wrCnt, mac_time_now());
+ break;
+
+ case L2I_HFS:
+ /*set_UINT32BE(&tag.wrCnt, ++fileref->l2_img.u.hfs.wrCnt);*/ /* ***TODO*** */
+ break;
+ }
+
+ err = image_write_tag(&fileref->l2_img->l1_img, block, &tag);
+ if (err)
+ return err;
+ }
+ len -= run_len;
+ src = (const UINT8 *)src + run_len;
+ fileref->crPs += run_len;
+ if ((fileref->crPs % 512) == 0)
+ fileref->reload_buf = TRUE;
+ }
+
+ return IMGTOOLERR_SUCCESS;
+}
+
+#ifdef UNUSED_FUNCTION
+/*
+ mac_file_tell
+
+ Get current position in an open mac file
+
+ fileref (I/O): mac file reference
+ filePos (O): current position in file
+
+ Return imgtool error code
+*/
+static imgtoolerr_t mac_file_tell(mac_fileref *fileref, UINT32 *filePos)
+{
+ *filePos = fileref->crPs;
+
+ return IMGTOOLERR_SUCCESS;
+}
+#endif
+
+/*
+ mac_file_seek
+
+ Set current position in an open mac file
+
+ fileref (I/O): mac file reference
+ filePos (I): new position in file
+
+ Return imgtool error code
+*/
+static imgtoolerr_t mac_file_seek(mac_fileref *fileref, UINT32 filePos)
+{
+ if ((fileref->crPs / 512) != (filePos / 512))
+ fileref->reload_buf = TRUE;
+
+ fileref->crPs = filePos;
+
+ return IMGTOOLERR_SUCCESS;
+}
+
+/*
+ mac_file_seteof
+
+ Set the position of the EOF in an open mac file
+
+ fileref (I/O): mac file reference
+ newEof (I): new position of EOF in file
+
+ Return imgtool error code
+*/
+static imgtoolerr_t mac_file_seteof(mac_fileref *fileref, UINT32 newEof)
+{
+ UINT32 newABEof;
+ imgtoolerr_t err = IMGTOOLERR_SUCCESS;
+
+ newABEof = (newEof + fileref->l2_img->blocksperAB * 512 - 1) / (fileref->l2_img->blocksperAB * 512);
+
+#if 0
+ if (fileref->pLen % (fileref->l2_img->blocksperAB * 512))
+ return IMGTOOLERR_CORRUPTIMAGE;
+#endif
+
+ if (newEof < fileref->eof)
+ fileref->eof = newEof;
+
+ switch (fileref->l2_img->format)
+ {
+ case L2I_MFS:
+ err = mfs_file_setABeof(fileref, newABEof);
+ break;
+
+ case L2I_HFS:
+ err = IMGTOOLERR_UNIMPLEMENTED;
+ break;
+ }
+ if (err)
+ return err;
+
+ fileref->eof = newEof;
+
+ err = mfs_dir_update(fileref);
+ if (err)
+ return err;
+
+ /* update current pos if beyond new EOF */
+#if 0
+ if (fileref->crPs > newEof)
+ {
+ if ((fileref->crPs / 512) != (newEof / 512))
+ fileref->reload_buf = TRUE;
+
+ fileref->crPs = newEof;
+ }
+#endif
+
+ return IMGTOOLERR_SUCCESS;
+}
+
+#if 0
+#pragma mark -
+#pragma mark MFS IMPLEMENTATION
+#endif
+
+/*
+ directory entry for use in the directory file
+
+ Note the structure is variable length. It is always word-aligned, and
+ cannot cross block boundaries.
+
+ Note that the directory does not seem to be sorted: the order in which
+ files appear does not match file names, and it does not always match file
+ IDs.
+*/
+typedef struct mfs_dir_entry
+{
+ UINT8 flags; /* bit 7=1 if entry used, bit 0=1 if file locked */
+ /* 0x00 means end of block: if we are not done
+ with reading the directory, the remnants will
+ be read from next block */
+ UINT8 flVersNum; /* version number (usually 0x00, but I don't
+ have the IM volume that describes it) */
+ mac_FInfo flFinderInfo; /* information used by the Finder */
+
+ UINT32BE fileID; /* file ID */
+
+ UINT16BE dataStartBlock; /* first allocation block of data fork */
+ UINT32BE dataLogicalSize; /* logical EOF of data fork */
+ UINT32BE dataPhysicalSize; /* physical EOF of data fork */
+ UINT16BE rsrcStartBlock; /* first allocation block of resource fork */
+ UINT32BE rsrcLogicalSize; /* logical EOF of resource fork */
+ UINT32BE rsrcPhysicalSize; /* physical EOF of resource fork */
+
+ UINT32BE createDate; /* date and time of creation */
+ UINT32BE modifyDate; /* date and time of last modification */
+
+ UINT8 name[1]; /* first char is length of file name */
+ /* next chars are file name - 255 chars at most */
+ /* IIRC, Finder 7 only supports 31 chars,
+ wheareas earlier versions support 63 chars */
+} mfs_dir_entry;
+
+/*
+ FOBJ desktop resource: describes a folder, or the location of the volume
+ icon.
+
+ In typical Apple manner, this resource is not documented. However, I have
+ managed to reverse engineer some parts of it.
+*/
+typedef struct mfs_FOBJ
+{
+ UINT8 unknown0[2]; /* $00: $0004 for disk, $0008 for folder??? */
+ mac_point location; /* $02: location in parent window */
+ UINT8 unknown1[4]; /* $06: ??? */
+ UINT8 view; /* $0A: manner in which folders are displayed??? */
+ UINT8 unknown2; /* $0B: ??? */
+ UINT16BE par_fldr; /* $0C: parent folder ID */
+ UINT8 unknown3[10]; /* $0E: ??? */
+ UINT16BE unknown4; /* $18: ??? */
+ UINT32BE createDate; /* $1A: date and time of creation */
+ UINT32BE modifyDate; /* $1E: date and time of last modification */
+ UINT16BE unknown5; /* $22: put-away folder ID?????? */
+ UINT8 unknown6[8]; /* $24: ??? */
+ mac_rect bounds; /* $2C: window bounds */
+ mac_point scroll; /* $34: current scroll offset??? */
+ union
+ { /* I think there are two versions of the structure */
+ struct
+ {
+ UINT16BE item_count; /* number of items (folders and files) in
+ this folder */
+ UINT32BE item_descs[1]; /* this variable-length array has
+ item_count entries - meaning of entry is unknown */
+ } v1;
+ struct
+ {
+ UINT16BE zerofill; /* always 0? */
+ UINT16BE unknown0; /* always 0??? */
+ UINT16BE item_count; /* number of items (folders and files) in
+ this folder */
+ UINT8 unknown1[20]; /* ??? */
+ UINT8 name[1]; /* variable-length macintosh string */
+ } v2;
+ } u;
+} mfs_FOBJ;
+
+/*
+ MFS open dir ref
+*/
+typedef struct mfs_dirref
+{
+ mac_l2_imgref *l2_img; /* image pointer */
+ UINT16 index; /* current file index in the disk directory */
+ UINT16 cur_block; /* current block offset in directory file */
+ UINT16 cur_offset; /* current byte offset in current block of directory file */
+ UINT8 block_buffer[512]; /* buffer with current directory block */
+} mfs_dirref;
+
+
+
+static imgtoolerr_t mfs_image_create(imgtool_image *image, imgtool_stream *stream, option_resolution *opts)
+{
+ imgtoolerr_t err;
+ UINT8 buffer[512];
+ UINT32 heads, tracks, sector_bytes, i;
+ UINT32 total_disk_blocks, total_allocation_blocks, allocation_block_size;
+ UINT32 free_allocation_blocks;
+
+ heads = option_resolution_lookup_int(opts, 'H');
+ tracks = option_resolution_lookup_int(opts, 'T');
+ sector_bytes = option_resolution_lookup_int(opts, 'L');
+
+ get_imgref(image)->l1_img.image = image;
+ get_imgref(image)->l1_img.heads = heads;
+
+ if (sector_bytes != 512)
+ return IMGTOOLERR_UNEXPECTED;
+
+ /* computation */
+ allocation_block_size = 1024;
+ total_disk_blocks = 0;
+ for (i = 0; i < tracks; i++)
+ total_disk_blocks += heads * apple35_sectors_per_track(imgtool_floppy(image), i) * sector_bytes / 512;
+ total_allocation_blocks = total_disk_blocks / (allocation_block_size / 512);
+ free_allocation_blocks = total_allocation_blocks - 3;
+
+ /* write master directory block */
+ memset(buffer, 0, sizeof(buffer));
+ place_integer_be(buffer, 0, 2, 0xd2d7); /* signature */
+ place_integer_be(buffer, 2, 4, mac_time_now()); /* creation date */
+ place_integer_be(buffer, 6, 4, mac_time_now()); /* last modified date */
+ place_integer_be(buffer, 10, 2, 0); /* volume attributes */
+ place_integer_be(buffer, 12, 2, 0); /* number of files in directory */
+ place_integer_be(buffer, 14, 2, 4); /* first block of directory */
+ place_integer_be(buffer, 16, 2, 12); /* length of directory in blocks */
+ place_integer_be(buffer, 18, 2, total_allocation_blocks); /* allocation blocks on volume count */
+ place_integer_be(buffer, 20, 4, allocation_block_size); /* allocation block size */
+ place_integer_be(buffer, 24, 4, 8192); /* default clumping size */
+ place_integer_be(buffer, 28, 2, 16); /* first allocation block on volume */
+ place_integer_be(buffer, 30, 4, 2); /* next unused catalog node */
+ place_integer_be(buffer, 34, 2, free_allocation_blocks); /* free allocation block count */
+ pascal_from_c_string(&buffer[36], 28, "Untitled"); /* volume title */
+
+ err = image_write_block(&get_imgref(image)->l1_img, 2, buffer);
+ if (err)
+ return err;
+
+ return IMGTOOLERR_SUCCESS;
+}
+
+
+
+/*
+ mfs_image_open
+
+ Open a MFS image. Image must already be open on level 1. This function
+ should not be called directly: call mac_image_open() instead.
+
+ l2_img (I/O): level-2 image reference to open (l1_img and format fields
+ must be initialized)
+ img_open_buf (I): buffer with the MDB block
+
+ Return imgtool error code
+*/
+static imgtoolerr_t mfs_image_open(imgtool_image *image, imgtool_stream *stream)
+{
+ imgtoolerr_t err;
+ mac_l2_imgref *l2_img;
+ img_open_buf buf_local;
+ img_open_buf *buf;
+
+ l2_img = get_imgref(image);
+ l2_img->l1_img.image = image;
+ l2_img->l1_img.heads = 1;
+ l2_img->format = L2I_MFS;
+
+ /* read MDB */
+ err = image_read_block(&l2_img->l1_img, 2, &buf_local.raw);
+ if (err)
+ return err;
+ buf = &buf_local;
+
+ /* check signature word */
+ if ((buf->mfs_mdb.sigWord[0] != 0xd2) || (buf->mfs_mdb.sigWord[1] != 0xd7)
+ || (buf->mfs_mdb.VN[0] > 27))
+ return IMGTOOLERR_CORRUPTIMAGE;
+
+ l2_img->u.mfs.dir_num_files = get_UINT16BE(buf->mfs_mdb.nmFls);
+ l2_img->u.mfs.dir_start = get_UINT16BE(buf->mfs_mdb.dirSt);
+ l2_img->u.mfs.dir_blk_len = get_UINT16BE(buf->mfs_mdb.blLn);
+
+ l2_img->numABs = get_UINT16BE(buf->mfs_mdb.nmAlBlks);
+ if ((get_UINT32BE(buf->mfs_mdb.alBlkSiz) % 512) || (get_UINT32BE(buf->mfs_mdb.alBlkSiz) == 0))
+ return IMGTOOLERR_CORRUPTIMAGE;
+ l2_img->blocksperAB = get_UINT32BE(buf->mfs_mdb.alBlkSiz) / 512;
+ l2_img->u.mfs.ABStart = get_UINT16BE(buf->mfs_mdb.alBlSt);
+
+ l2_img->nxtCNID = get_UINT32BE(buf->mfs_mdb.nxtFNum);
+
+ l2_img->freeABs = get_UINT16BE(buf->mfs_mdb.freeABs);
+
+ mac_strcpy(l2_img->u.mfs.volname, buf->mfs_mdb.VN);
+
+ if (l2_img->numABs > 4094)
+ return IMGTOOLERR_CORRUPTIMAGE;
+
+ /* extract link array */
+ {
+ int byte_len = l2_img->numABs + ((l2_img->numABs + 1) >> 1);
+ int cur_byte;
+ int cur_block;
+ int block_len = sizeof(buf->mfs_mdb.ABlink);
+
+ /* clear dirty flags */
+ for (cur_block=0; cur_block<13; cur_block++)
+ l2_img->u.mfs.ABlink_dirty[cur_block] = 0;
+
+ /* append the chunk after MDB to link array */
+ cur_byte = 0;
+ if (block_len > (byte_len - cur_byte))
+ block_len = byte_len - cur_byte;
+ memcpy(l2_img->u.mfs.ABlink+cur_byte, buf->mfs_mdb.ABlink, block_len);
+ cur_byte += block_len;
+ cur_block = 2;
+ while (cur_byte < byte_len)
+ {
+ /* read next block */
+ cur_block++;
+ err = image_read_block(&l2_img->l1_img, cur_block, buf->raw);
+ if (err)
+ return err;
+ block_len = 512;
+
+ /* append this block to link array */
+ if (block_len > (byte_len - cur_byte))
+ block_len = byte_len - cur_byte;
+ memcpy(l2_img->u.mfs.ABlink+cur_byte, buf->raw, block_len);
+ cur_byte += block_len;
+ }
+ /* check that link array and directory don't overlap */
+ if (cur_block >= l2_img->u.mfs.dir_start)
+ return IMGTOOLERR_CORRUPTIMAGE;
+ }
+
+ return IMGTOOLERR_SUCCESS;
+}
+
+/*
+ mfs_update_mdb
+
+ Update MDB on disk
+
+ l2_img (I/O): level-2 image reference
+
+ Return imgtool error code
+*/
+static imgtoolerr_t mfs_update_mdb(mac_l2_imgref *l2_img)
+{
+ imgtoolerr_t err;
+ union
+ {
+ mfs_mdb_t mfs_mdb;
+ UINT8 raw[512];
+ } buf;
+
+ assert(l2_img->format == L2I_MFS);
+
+ /* read MDB */
+ err = image_read_block(&l2_img->l1_img, 2, &buf.mfs_mdb);
+ if (err)
+ return err;
+
+ set_UINT16BE(&buf.mfs_mdb.nmFls, l2_img->u.mfs.dir_num_files);
+#if 0 /* these fields are never changed */
+ set_UINT16BE(&buf.mfs_mdb.dirSt, l2_img->u.mfs.dir_start);
+ set_UINT16BE(&buf.mfs_mdb.blLn, l2_img->u.mfs.dir_blk_len);
+
+ set_UINT16BE(&buf.mfs_mdb.nmAlBlks, l2_img->numABs);
+ set_UINT32BE(&buf.mfs_mdb.alBlkSiz, l2_img->blocksperAB*512);
+ set_UINT16BE(&buf.mfs_mdb.alBlSt, l2_img->u.mfs.ABStart);
+#endif
+
+ set_UINT32BE(&buf.mfs_mdb.nxtFNum, l2_img->nxtCNID);
+
+ set_UINT16BE(&buf.mfs_mdb.freeABs, l2_img->freeABs);
+
+#if 0 /* these fields are never changed */
+ mac_strcpy(buf.mfs_mdb.VN, l2_img->u.mfs.volname);
+#endif
+
+ /* save link array */
+ {
+ int byte_len = l2_img->numABs + ((l2_img->numABs + 1) >> 1);
+ int cur_byte = 0;
+ int cur_block = 2;
+ int block_len = sizeof(buf.mfs_mdb.ABlink);
+
+ /* update the chunk of link array after the MDB */
+ if (block_len > (byte_len - cur_byte))
+ block_len = byte_len - cur_byte;
+ memcpy(buf.mfs_mdb.ABlink, l2_img->u.mfs.ABlink+cur_byte, block_len);
+ cur_byte += block_len;
+
+ if (block_len < sizeof(buf.mfs_mdb.ABlink))
+ memset(buf.mfs_mdb.ABlink+block_len, 0, sizeof(buf.mfs_mdb.ABlink)-block_len);
+
+ l2_img->u.mfs.ABlink_dirty[0] = 0;
+
+ /* write back modified MDB+link */
+ err = image_write_block(&l2_img->l1_img, 2, &buf.mfs_mdb);
+ if (err)
+ return err;
+
+ while (cur_byte < byte_len)
+ {
+ /* advance to next block */
+ cur_block++;
+ block_len = 512;
+
+ /* extract the current chunk of link array */
+ if (block_len > (byte_len - cur_byte))
+ block_len = byte_len - cur_byte;
+
+ if (l2_img->u.mfs.ABlink_dirty[cur_block-2])
+ {
+ memcpy(buf.raw, l2_img->u.mfs.ABlink+cur_byte, block_len);
+ if (block_len < 512)
+ memset(buf.raw+block_len, 0, 512-block_len);
+ /* write back link array */
+ err = image_write_block(&l2_img->l1_img, cur_block, buf.raw);
+ if (err)
+ return err;
+ l2_img->u.mfs.ABlink_dirty[cur_block-2] = 0;
+ }
+
+ cur_byte += block_len;
+ }
+ }
+
+ return IMGTOOLERR_SUCCESS;
+}
+
+/*
+ mfs_dir_open
+
+ Open the directory file
+
+ l2_img (I/O): level-2 image reference
+ dirref (O): open directory file reference
+
+ Return imgtool error code
+*/
+static imgtoolerr_t mfs_dir_open(mac_l2_imgref *l2_img, const char *path, mfs_dirref *dirref)
+{
+ imgtoolerr_t err;
+
+ assert(l2_img->format == L2I_MFS);
+
+ if (path[0])
+ return IMGTOOLERR_PATHNOTFOUND;
+
+ dirref->l2_img = l2_img;
+ dirref->index = 0;
+
+ dirref->cur_block = 0;
+ dirref->cur_offset = 0;
+ err = image_read_block(&l2_img->l1_img, l2_img->u.mfs.dir_start + dirref->cur_block, dirref->block_buffer);
+ if (err)
+ return err;
+
+ return IMGTOOLERR_SUCCESS;
+}
+
+/*
+ mfs_dir_read
+
+ Read one entry of directory file
+
+ dirref (I/O): open directory file reference
+ dir_entry (O): set to point to the entry read: set to NULL if EOF or error
+
+ Return imgtool error code
+*/
+static imgtoolerr_t mfs_dir_read(mfs_dirref *dirref, mfs_dir_entry **dir_entry)
+{
+ mfs_dir_entry *cur_dir_entry;
+ size_t cur_dir_entry_len;
+ imgtoolerr_t err;
+
+
+ if (dir_entry)
+ *dir_entry = NULL;
+
+ if (dirref->index == dirref->l2_img->u.mfs.dir_num_files)
+ /* EOF */
+ return IMGTOOLERR_SUCCESS;
+
+ /* get cat entry pointer */
+ cur_dir_entry = (mfs_dir_entry *) (dirref->block_buffer + dirref->cur_offset);
+ while ((dirref->cur_offset == 512) || ! (cur_dir_entry->flags & 0x80))
+ {
+ dirref->cur_block++;
+ dirref->cur_offset = 0;
+ if (dirref->cur_block > dirref->l2_img->u.mfs.dir_blk_len)
+ /* aargh! */
+ return IMGTOOLERR_CORRUPTIMAGE;
+ err = image_read_block(&dirref->l2_img->l1_img, dirref->l2_img->u.mfs.dir_start + dirref->cur_block, dirref->block_buffer);
+ if (err)
+ return err;
+ cur_dir_entry = (mfs_dir_entry *) (dirref->block_buffer + dirref->cur_offset);
+ }
+
+ cur_dir_entry_len = offsetof(mfs_dir_entry, name) + cur_dir_entry->name[0] + 1;
+
+ if ((dirref->cur_offset + cur_dir_entry_len) > 512)
+ /* aargh! */
+ return IMGTOOLERR_CORRUPTIMAGE;
+
+ /* entry looks valid: set pointer to entry */
+ if (dir_entry)
+ *dir_entry = cur_dir_entry;
+
+ /* update offset in block */
+ dirref->cur_offset += cur_dir_entry_len;
+ /* align to word boundary */
+ dirref->cur_offset = (dirref->cur_offset + 1) & ~1;
+
+ /* update file count */
+ dirref->index++;
+
+ return IMGTOOLERR_SUCCESS;
+}
+
+/*
+ mfs_dir_insert
+
+ Add an entry in the directory file
+
+ l2_img (I/O): level-2 image reference
+ dirref (I/O): open directory file reference
+ filename (I): name of the file for which an entry is created (Mac string)
+ dir_entry (O): set to point to the created entry: set to NULL if EOF or
+ error
+
+ Return imgtool error code
+*/
+static imgtoolerr_t mfs_dir_insert(mac_l2_imgref *l2_img, mfs_dirref *dirref, const UINT8 *new_fname, mfs_dir_entry **dir_entry)
+{
+ size_t new_dir_entry_len;
+ mfs_dir_entry *cur_dir_entry;
+ size_t cur_dir_entry_len;
+ UINT32 cur_date;
+ imgtoolerr_t err;
+
+ dirref->l2_img = l2_img;
+ dirref->index = 0;
+
+ new_dir_entry_len = offsetof(mfs_dir_entry, name) + new_fname[0] + 1;
+
+ for (dirref->cur_block = 0; dirref->cur_block < dirref->l2_img->u.mfs.dir_blk_len; dirref->cur_block++)
+ {
+ /* read current block */
+ err = image_read_block(&dirref->l2_img->l1_img, dirref->l2_img->u.mfs.dir_start + dirref->cur_block, dirref->block_buffer);
+ if (err)
+ return err;
+
+ /* get free chunk in this block */
+ dirref->cur_offset = 0;
+ cur_dir_entry = (mfs_dir_entry *) (dirref->block_buffer + dirref->cur_offset);
+ while ((dirref->cur_offset < 512) && (cur_dir_entry->flags & 0x80))
+ { /* skip cur_dir_entry */
+ cur_dir_entry_len = offsetof(mfs_dir_entry, name) + cur_dir_entry->name[0] + 1;
+ /* update offset in block */
+ dirref->cur_offset += cur_dir_entry_len;
+ /* align to word boundary */
+ dirref->cur_offset = (dirref->cur_offset + 1) & ~1;
+ /* update entry pointer */
+ cur_dir_entry = (mfs_dir_entry *) (dirref->block_buffer + dirref->cur_offset);
+ /* update file index (useless, but can't harm) */
+ dirref->index++;
+ }
+
+ if (new_dir_entry_len <= (/*512*/510 - dirref->cur_offset))
+ {
+ /*memcpy(cur_dir_entry, new_dir_entry, new_dir_entry_len);*/
+ cur_dir_entry->flags = 0x80;
+ cur_dir_entry->flVersNum = 0x00;
+ memset(&cur_dir_entry->flFinderInfo, 0, sizeof(cur_dir_entry->flFinderInfo));
+ set_UINT32BE(&cur_dir_entry->fileID, dirref->l2_img->nxtCNID++);
+ set_UINT16BE(&cur_dir_entry->dataStartBlock, 1);
+ set_UINT32BE(&cur_dir_entry->dataLogicalSize, 0);
+ set_UINT32BE(&cur_dir_entry->dataPhysicalSize, 0);
+ set_UINT16BE(&cur_dir_entry->rsrcStartBlock, 1);
+ set_UINT32BE(&cur_dir_entry->rsrcLogicalSize, 0);
+ set_UINT32BE(&cur_dir_entry->rsrcPhysicalSize, 0);
+ cur_date = mac_time_now();
+ set_UINT32BE(&cur_dir_entry->createDate, cur_date);
+ set_UINT32BE(&cur_dir_entry->modifyDate, cur_date);
+ mac_strcpy(cur_dir_entry->name, new_fname);
+
+ /* update offset in block */
+ dirref->cur_offset += new_dir_entry_len;
+ /* align to word boundary */
+ dirref->cur_offset = (dirref->cur_offset + 1) & ~1;
+ if (dirref->cur_offset < 512)
+ /* mark remaining space as free record */
+ dirref->block_buffer[dirref->cur_offset] = 0;
+ /* write back directory */
+ err = image_write_block(&dirref->l2_img->l1_img, dirref->l2_img->u.mfs.dir_start + dirref->cur_block, dirref->block_buffer);
+ if (err)
+ return err;
+ /* update file count */
+ dirref->l2_img->u.mfs.dir_num_files++;
+
+ /* update MDB (nxtCNID & dir_num_files fields) */
+ err = mfs_update_mdb(dirref->l2_img);
+ if (err)
+ return err;
+
+ if (dir_entry)
+ *dir_entry = cur_dir_entry;
+ return IMGTOOLERR_SUCCESS;
+ }
+ }
+
+ return IMGTOOLERR_NOSPACE;
+}
+
+/*
+ mfs_dir_update
+
+ Update one entry of directory file
+
+ fileref (I/O): open file reference
+
+ Return imgtool error code
+*/
+static imgtoolerr_t mfs_dir_update(mac_fileref *fileref)
+{
+ UINT16 cur_block;
+ UINT16 cur_offset;
+ UINT8 block_buffer[512];
+ mfs_dir_entry *cur_dir_entry;
+ size_t cur_dir_entry_len;
+ imgtoolerr_t err;
+
+ for (cur_block = 0; cur_block < fileref->l2_img->u.mfs.dir_blk_len; cur_block++)
+ {
+ /* read current block */
+ err = image_read_block(&fileref->l2_img->l1_img, fileref->l2_img->u.mfs.dir_start + cur_block, block_buffer);
+ if (err)
+ return err;
+
+ /* get free chunk in this block */
+ cur_offset = 0;
+ cur_dir_entry = (mfs_dir_entry *) (block_buffer + cur_offset);
+ while ((cur_offset < 512) && (cur_dir_entry->flags & 0x80))
+ {
+ if (get_UINT32BE(cur_dir_entry->fileID) == fileref->fileID)
+ { /* found it: update directory entry */
+ switch (fileref->forkType)
+ {
+ case data_fork:
+ set_UINT16BE(&cur_dir_entry->dataStartBlock, fileref->u.mfs.stBlk);
+ set_UINT32BE(&cur_dir_entry->dataLogicalSize, fileref->eof);
+ set_UINT32BE(&cur_dir_entry->dataPhysicalSize, fileref->pLen);
+ break;
+
+ case rsrc_fork:
+ set_UINT16BE(&cur_dir_entry->rsrcStartBlock, fileref->u.mfs.stBlk);
+ set_UINT32BE(&cur_dir_entry->rsrcLogicalSize, fileref->eof);
+ set_UINT32BE(&cur_dir_entry->rsrcPhysicalSize, fileref->pLen);
+ break;
+ }
+ /* write back directory */
+ err = image_write_block(&fileref->l2_img->l1_img, fileref->l2_img->u.mfs.dir_start + cur_block, block_buffer);
+ if (err)
+ return err;
+
+ return IMGTOOLERR_SUCCESS;
+ }
+ /* skip cur_dir_entry */
+ cur_dir_entry_len = offsetof(mfs_dir_entry, name) + cur_dir_entry->name[0] + 1;
+ /* update offset in block */
+ cur_offset += cur_dir_entry_len;
+ /* align to word boundary */
+ cur_offset = (cur_offset + 1) & ~1;
+ /* update entry pointer */
+ cur_dir_entry = (mfs_dir_entry *) (block_buffer + cur_offset);
+ }
+ }
+
+ return IMGTOOLERR_UNEXPECTED;
+}
+
+
+/*
+ mfs_find_dir_entry
+
+ Find a file in an MFS directory
+
+ dirref (I/O): open directory file reference
+ filename (I): file name (Mac string)
+ dir_entry (O): set to point to the entry read: set to NULL if EOF or error
+
+ Return imgtool error code
+*/
+static imgtoolerr_t mfs_find_dir_entry(mfs_dirref *dirref, const mac_str255 filename, mfs_dir_entry **dir_entry)
+{
+ mfs_dir_entry *cur_dir_entry;
+ imgtoolerr_t err;
+
+ if (dir_entry)
+ *dir_entry = NULL;
+
+ /* scan dir for file */
+ while (1)
+ {
+ err = mfs_dir_read(dirref, &cur_dir_entry);
+ if (err)
+ return err;
+ if (!cur_dir_entry)
+ /* EOF */
+ break;
+ if ((! mac_stricmp(filename, cur_dir_entry->name)) && (cur_dir_entry->flVersNum == 0))
+ { /* file found */
+
+ if (dir_entry)
+ *dir_entry = cur_dir_entry;
+
+ return IMGTOOLERR_SUCCESS;
+ }
+ }
+
+ return IMGTOOLERR_FILENOTFOUND;
+}
+
+/*
+ mfs_lookup_path
+
+ Resolve a file path for MFS volumes. This function should not be called
+ directly: call mac_lookup_path instead.
+
+ l2_img (I/O): level-2 image reference
+ fpath (I): file path (C string)
+ filename (O): set to the actual name of the file, with capitalization matching
+ the one on the volume rather than the one in the fpath parameter (Mac
+ string)
+ cat_info (I/O): on output, catalog info for this file extracted from the
+ catalog file (may be NULL)
+ If create_it is TRUE, created info will first be set according to the
+ data from cat_info
+ create_it (I): TRUE if entry should be created if not found
+
+ Return imgtool error code
+*/
+static imgtoolerr_t mfs_lookup_path(mac_l2_imgref *l2_img, const char *fpath, mac_str255 filename, mac_dirent *cat_info, int create_it)
+{
+ mfs_dirref dirref;
+ mfs_dir_entry *dir_entry;
+ imgtoolerr_t err;
+
+ /* rapid check */
+ if (strchr(fpath, ':'))
+ return IMGTOOLERR_BADFILENAME;
+
+ /* extract file name */
+ c_to_mac_strncpy(filename, fpath, strlen(fpath));
+
+ /* open dir */
+ mfs_dir_open(l2_img, "", &dirref);
+
+ /* find file */
+ err = mfs_find_dir_entry(&dirref, filename, &dir_entry);
+ if ((err == IMGTOOLERR_FILENOTFOUND) && create_it)
+ err = mfs_dir_insert(l2_img, &dirref, filename, &dir_entry);
+ if (err)
+ return err;
+
+ mac_strcpy(filename, dir_entry->name);
+
+ if (create_it && cat_info)
+ {
+ dir_entry->flFinderInfo = cat_info->flFinderInfo;
+ dir_entry->flags = (dir_entry->flags & 0x80) | (cat_info->flags & 0x7f);
+ set_UINT32BE(&dir_entry->createDate, cat_info->createDate);
+ set_UINT32BE(&dir_entry->modifyDate, cat_info->modifyDate);
+
+ /* write current directory block */
+ err = image_write_block(&l2_img->l1_img, l2_img->u.mfs.dir_start + dirref.cur_block, dirref.block_buffer);
+ if (err)
+ return err;
+ }
+
+ if (cat_info)
+ {
+ cat_info->flFinderInfo = dir_entry->flFinderInfo;
+ memset(&cat_info->flXFinderInfo, 0, sizeof(cat_info->flXFinderInfo));
+ cat_info->flags = dir_entry->flags;
+ cat_info->fileID = get_UINT32BE(dir_entry->fileID);
+ cat_info->dataLogicalSize = get_UINT32BE(dir_entry->dataLogicalSize);
+ cat_info->dataPhysicalSize = get_UINT32BE(dir_entry->dataPhysicalSize);
+ cat_info->rsrcLogicalSize = get_UINT32BE(dir_entry->rsrcLogicalSize);
+ cat_info->rsrcPhysicalSize = get_UINT32BE(dir_entry->rsrcPhysicalSize);
+ cat_info->createDate = get_UINT32BE(dir_entry->createDate);
+ cat_info->modifyDate = get_UINT32BE(dir_entry->modifyDate);
+ cat_info->dataRecType = 0x200; /* hcrt_File */
+ }
+
+ return IMGTOOLERR_SUCCESS;
+}
+
+/*
+ mfs_file_open_internal
+
+ Open a file fork, given its directory entry. This function should not be
+ called directly: call mfs_file_open instead.
+
+ l2_img (I/O): level-2 image reference
+ dir_entry (I): directory entry for the file to open
+ mac_forkID (I): tells which fork should be opened
+ fileref (O): mac file reference to open
+
+ Return imgtool error code
+*/
+static imgtoolerr_t mfs_file_open_internal(mac_l2_imgref *l2_img, const mfs_dir_entry *dir_entry, mac_forkID fork, mac_fileref *fileref)
+{
+ assert(l2_img->format == L2I_MFS);
+
+ fileref->l2_img = l2_img;
+
+ fileref->fileID = get_UINT32BE(dir_entry->fileID);
+ fileref->forkType = fork;
+
+ switch (fork)
+ {
+ case data_fork:
+ fileref->u.mfs.stBlk = get_UINT16BE(dir_entry->dataStartBlock);
+ fileref->eof = get_UINT32BE(dir_entry->dataLogicalSize);
+ fileref->pLen = get_UINT32BE(dir_entry->dataPhysicalSize);
+ break;
+
+ case rsrc_fork:
+ fileref->u.mfs.stBlk = get_UINT16BE(dir_entry->rsrcStartBlock);
+ fileref->eof = get_UINT32BE(dir_entry->rsrcLogicalSize);
+ fileref->pLen = get_UINT32BE(dir_entry->rsrcPhysicalSize);
+ break;
+ }
+
+ fileref->crPs = 0;
+
+ fileref->reload_buf = TRUE;
+
+ return IMGTOOLERR_SUCCESS;
+}
+
+/*
+ mfs_file_open
+
+ Open a file located on a MFS volume. This function should not be called
+ directly: call mac_file_open instead.
+
+ l2_img (I/O): level-2 image reference
+ filename (I): name of the file (Mac string)
+ mac_forkID (I): tells which fork should be opened
+ fileref (O): mac file reference to open
+
+ Return imgtool error code
+*/
+static imgtoolerr_t mfs_file_open(mac_l2_imgref *l2_img, const mac_str255 filename, mac_forkID fork, mac_fileref *fileref)
+{
+ mfs_dirref dirref;
+ mfs_dir_entry *dir_entry;
+ imgtoolerr_t err;
+
+ /* open dir */
+ mfs_dir_open(l2_img, "", &dirref);
+
+ /* find file */
+ err = mfs_find_dir_entry(&dirref, filename, &dir_entry);
+ if (err)
+ return err;
+
+ /* open it */
+ return mfs_file_open_internal(l2_img, dir_entry, fork, fileref);
+}
+
+/*
+ mfs_get_ABlink
+
+ Read one entry of the Allocation Bitmap link array, on an MFS volume.
+
+ l2_img (I/O): level-2 image reference
+ AB_address (I): index in the array, which is an AB address
+
+ Returns the 12-bit value read in array.
+*/
+static UINT16 mfs_get_ABlink(mac_l2_imgref *l2_img, UINT16 AB_address)
+{
+ UINT16 reply;
+ int base;
+
+ assert(l2_img->format == L2I_MFS);
+
+ base = (AB_address >> 1) * 3;
+
+ if (! (AB_address & 1))
+ reply = (l2_img->u.mfs.ABlink[base] << 4) | ((l2_img->u.mfs.ABlink[base+1] >> 4) & 0x0f);
+ else
+ reply = ((l2_img->u.mfs.ABlink[base+1] << 8) & 0xf00) | l2_img->u.mfs.ABlink[base+2];
+
+ return reply;
+}
+
+/*
+ mfs_set_ABlink
+
+ Set one entry of the Allocation Bitmap link array, on an MFS volume.
+
+ l2_img (I/O): level-2 image reference
+ AB_address (I): index in the array, which is an AB address
+ data (I): 12-bit value to write in array
+*/
+static void mfs_set_ABlink(mac_l2_imgref *l2_img, UINT16 AB_address, UINT16 data)
+{
+ int base;
+
+ assert(l2_img->format == L2I_MFS);
+
+ base = (AB_address >> 1) * 3;
+
+ if (! (AB_address & 1))
+ {
+ l2_img->u.mfs.ABlink[base] = (data >> 4) & 0xff;
+ l2_img->u.mfs.ABlink[base+1] = (l2_img->u.mfs.ABlink[base+1] & 0x0f) | ((data << 4) & 0xf0);
+
+ l2_img->u.mfs.ABlink_dirty[(base+64)/512] = 1;
+ l2_img->u.mfs.ABlink_dirty[(base+1+64)/512] = 1;
+ }
+ else
+ {
+ l2_img->u.mfs.ABlink[base+1] = (l2_img->u.mfs.ABlink[base+1] & 0xf0) | ((data >> 8) & 0x0f);
+ l2_img->u.mfs.ABlink[base+2] = data & 0xff;
+
+ l2_img->u.mfs.ABlink_dirty[(base+1+64)/512] = 1;
+ l2_img->u.mfs.ABlink_dirty[(base+2+64)/512] = 1;
+ }
+}
+
+/*
+ mfs_file_get_nth_block_address
+
+ Get the disk block address of a given block in an open file on a MFS image.
+ Called by macintosh file code.
+
+ fileref (I/O): open mac file reference
+ block_num (I): file block index
+ block_address (O): disk block address for the file block
+
+ Return imgtool error code
+*/
+static imgtoolerr_t mfs_file_get_nth_block_address(mac_fileref *fileref, UINT32 block_num, UINT32 *block_address)
+{
+ UINT32 AB_num;
+ UINT32 i;
+ UINT16 AB_address;
+
+ assert(fileref->l2_img->format == L2I_MFS);
+
+ AB_num = block_num / fileref->l2_img->blocksperAB;
+
+ AB_address = fileref->u.mfs.stBlk;
+ if ((AB_address == 0) || (AB_address >= fileref->l2_img->numABs+2))
+ /* 0 -> ??? */
+ return IMGTOOLERR_CORRUPTIMAGE;
+ if (AB_address == 1)
+ /* EOF */
+ return IMGTOOLERR_UNEXPECTED;
+ AB_address -= 2;
+ for (i=0; i<AB_num; i++)
+ {
+ AB_address = mfs_get_ABlink(fileref->l2_img, AB_address);
+ if ((AB_address == 0) || (AB_address >= fileref->l2_img->numABs+2))
+ /* 0 -> empty block: there is no way an empty block could make it
+ into the link chain!!! */
+ return IMGTOOLERR_CORRUPTIMAGE;
+ if (AB_address == 1)
+ /* EOF */
+ return IMGTOOLERR_UNEXPECTED;
+ AB_address -= 2;
+ }
+
+ *block_address = fileref->l2_img->u.mfs.ABStart + AB_address * fileref->l2_img->blocksperAB
+ + block_num % fileref->l2_img->blocksperAB;
+
+ return IMGTOOLERR_SUCCESS;
+}
+
+/*
+ mfs_file_allocABs
+
+ Allocate a chunk of ABs
+
+ fileref (I/O): open mac file reference
+ lastAB (I): AB address on disk of last file AB (only if
+ fileref->u.mfs.stBlk != 1)
+ allocABs (I): number of ABs to allocate in addition to the current file
+ allocation
+ fblock (I): first file block to allocate (used for tag data)
+
+ Return imgtool error code
+*/
+static imgtoolerr_t mfs_file_allocABs(mac_fileref *fileref, UINT16 lastAB, UINT32 allocABs, UINT32 fblock)
+{
+ int numABs = fileref->l2_img->numABs;
+ int free_ABs;
+ int i, j;
+ floppy_tag_record tag;
+ int extentBaseAB, extentABlen;
+ int firstBestExtentBaseAB = 0, firstBestExtentABlen;
+ int secondBestExtentBaseAB = 0, secondBestExtentABlen;
+ imgtoolerr_t err;
+
+ /* return if done */
+ if (! allocABs)
+ return IMGTOOLERR_SUCCESS;
+
+ /* compute free space */
+ free_ABs = 0;
+ for (i=0; i<numABs; i++)
+ {
+ if (mfs_get_ABlink(fileref->l2_img, i) == 0)
+ {
+ if (TAG_CHECKS)
+ {
+ /* optional check */
+ if (image_get_tag_len(&fileref->l2_img->l1_img) == 12)
+ {
+ for (j=0; j<fileref->l2_img->blocksperAB; j++)
+ {
+ err = image_read_tag(&fileref->l2_img->l1_img, fileref->l2_img->u.mfs.ABStart + i * fileref->l2_img->blocksperAB + j, &tag);
+ if (err)
+ return err;
+
+ if (get_UINT32BE(tag.fileID) != 0)
+ {
+ /*return IMGTOOLERR_CORRUPTIMAGE;*/
+ goto corrupt_free_block;
+ }
+ }
+ }
+ }
+
+ free_ABs++;
+ }
+corrupt_free_block:
+ ;
+ }
+
+ /* check we have enough free space */
+ if (free_ABs < allocABs)
+ return IMGTOOLERR_NOSPACE;
+
+ if (fileref->u.mfs.stBlk != 1)
+ { /* try to extend last file extent */
+ /* append free ABs after last AB */
+ for (i=lastAB+1; (mfs_get_ABlink(fileref->l2_img, i) == 0) && (allocABs > 0) && (i < numABs); i++)
+ {
+ if (TAG_CHECKS)
+ {
+ /* optional check */
+ if (image_get_tag_len(&fileref->l2_img->l1_img) == 12)
+ {
+ for (j=0; j<fileref->l2_img->blocksperAB; j++)
+ {
+ err = image_read_tag(&fileref->l2_img->l1_img, fileref->l2_img->u.mfs.ABStart + i * fileref->l2_img->blocksperAB + j, &tag);
+ if (err)
+ return err;
+
+ if (get_UINT32BE(tag.fileID) != 0)
+ {
+ /*return IMGTOOLERR_CORRUPTIMAGE;*/
+ goto corrupt_free_block2;
+ }
+ }
+ }
+ }
+
+ mfs_set_ABlink(fileref->l2_img, lastAB, i+2);
+ lastAB = i;
+ allocABs--;
+ free_ABs--;
+ }
+corrupt_free_block2:
+ /* return if done */
+ if (! allocABs)
+ {
+ mfs_set_ABlink(fileref->l2_img, lastAB, 1);
+ fileref->l2_img->freeABs = free_ABs;
+ return IMGTOOLERR_SUCCESS; /* done */
+ }
+ }
+
+ while (allocABs)
+ {
+ /* find smallest data block at least nb_alloc_physrecs in length, and largest data block less than nb_alloc_physrecs in length */
+ firstBestExtentABlen = INT_MAX;
+ secondBestExtentABlen = 0;
+ for (i=0; i<numABs; i++)
+ {
+ if (mfs_get_ABlink(fileref->l2_img, i) == 0)
+ { /* found one free block */
+ /* compute its length */
+ extentBaseAB = i;
+ extentABlen = 0;
+ while ((i<numABs) && (mfs_get_ABlink(fileref->l2_img, i) == 0))
+ {
+ if (TAG_CHECKS)
+ {
+ /* optional check */
+ if (image_get_tag_len(&fileref->l2_img->l1_img) == 12)
+ {
+ for (j=0; j<fileref->l2_img->blocksperAB; j++)
+ {
+ err = image_read_tag(&fileref->l2_img->l1_img, fileref->l2_img->u.mfs.ABStart + i * fileref->l2_img->blocksperAB + j, &tag);
+ if (err)
+ return err;
+
+ if (get_UINT32BE(tag.fileID) != 0)
+ {
+ /*return IMGTOOLERR_CORRUPTIMAGE;*/
+ goto corrupt_free_block3;
+ }
+ }
+ }
+ }
+
+ extentABlen++;
+ i++;
+ }
+corrupt_free_block3:
+ /* compare to previous best and second-best blocks */
+ if ((extentABlen < firstBestExtentABlen) && (extentABlen >= allocABs))
+ {
+ firstBestExtentBaseAB = extentBaseAB;
+ firstBestExtentABlen = extentABlen;
+ if (extentABlen == allocABs)
+ /* no need to search further */
+ break;
+ }
+ else if ((extentABlen > secondBestExtentABlen) && (extentABlen < allocABs))
+ {
+ secondBestExtentBaseAB = extentBaseAB;
+ secondBestExtentABlen = extentABlen;
+ }
+ }
+ }
+
+ if (firstBestExtentABlen != INT_MAX)
+ { /* found one contiguous block which can hold it all */
+ extentABlen = allocABs;
+ for (i=0; i<allocABs; i++)
+ {
+ if (fileref->u.mfs.stBlk != 1)
+ mfs_set_ABlink(fileref->l2_img, lastAB, firstBestExtentBaseAB+i+2);
+ else
+ fileref->u.mfs.stBlk = firstBestExtentBaseAB+i+2;
+ lastAB = firstBestExtentBaseAB+i;
+ free_ABs--;
+ /* set tag to allocated */
+ if (image_get_tag_len(&fileref->l2_img->l1_img) == 12)
+ {
+ set_UINT32BE(&tag.fileID, fileref->fileID);
+ tag.ftype = 1;
+ if ((fileref->forkType) == rsrc_fork)
+ tag.ftype |= 2;
+ tag.fattr = /*fattr*/ 0; /* ***TODO*** */
+ for (j=0; j<fileref->l2_img->blocksperAB; j++)
+ {
+ set_UINT16BE(&tag.fblock, fblock & 0xffff);
+ set_UINT32BE(&tag.wrCnt, mac_time_now());
+ err = image_write_tag(&fileref->l2_img->l1_img, fileref->l2_img->u.mfs.ABStart + lastAB * fileref->l2_img->blocksperAB + j, &tag);
+ if (err)
+ {
+ mfs_set_ABlink(fileref->l2_img, lastAB, 1);
+ fileref->l2_img->freeABs = free_ABs;
+ return err;
+ }
+ fblock++;
+ }
+ }
+ }
+ allocABs = 0;
+ mfs_set_ABlink(fileref->l2_img, lastAB, 1);
+ fileref->l2_img->freeABs = free_ABs;
+ /*return IMGTOOLERR_SUCCESS;*/ /* done */
+ }
+ else if (secondBestExtentABlen != 0)
+ { /* jeez, we need to fragment it. We use the largest smaller block to limit fragmentation. */
+ for (i=0; i<secondBestExtentABlen; i++)
+ {
+ if (fileref->u.mfs.stBlk != 1)
+ mfs_set_ABlink(fileref->l2_img, lastAB, secondBestExtentBaseAB+i+2);
+ else
+ fileref->u.mfs.stBlk = secondBestExtentBaseAB+i+2;
+ lastAB = secondBestExtentBaseAB+i;
+ free_ABs--;
+ /* set tag to allocated */
+ if (image_get_tag_len(&fileref->l2_img->l1_img) == 12)
+ {
+ set_UINT32BE(&tag.fileID, fileref->fileID);
+ tag.ftype = 1;
+ if ((fileref->forkType) == rsrc_fork)
+ tag.ftype |= 2;
+ tag.fattr = /*fattr*/ 0; /* ***TODO*** */
+ for (j=0; j<fileref->l2_img->blocksperAB; j++)
+ {
+ set_UINT16BE(&tag.fblock, fblock & 0xffff);
+ set_UINT32BE(&tag.wrCnt, mac_time_now());
+ err = image_write_tag(&fileref->l2_img->l1_img, fileref->l2_img->u.mfs.ABStart + lastAB * fileref->l2_img->blocksperAB + j, &tag);
+ if (err)
+ {
+ mfs_set_ABlink(fileref->l2_img, lastAB, 1);
+ fileref->l2_img->freeABs = free_ABs;
+ return err;
+ }
+ fblock++;
+ }
+ }
+ }
+ allocABs -= secondBestExtentABlen;
+ }
+ else
+ {
+ mfs_set_ABlink(fileref->l2_img, lastAB, 1);
+ return IMGTOOLERR_NOSPACE; /* This should never happen, as we pre-check that there is enough free space */
+ }
+ }
+
+ return IMGTOOLERR_SUCCESS;
+}
+
+/*
+ mfs_file_setABeof
+
+ Set physical file EOF in ABs
+
+ fileref (I/O): open mac file reference
+ newABeof (I): desired number of allocated ABs for this file
+
+ Return imgtool error code
+*/
+static imgtoolerr_t mfs_file_setABeof(mac_fileref *fileref, UINT32 newABeof)
+{
+ UINT16 AB_address = 0;
+ UINT16 AB_link;
+ int i, j;
+ floppy_tag_record tag;
+ int MDB_dirty = 0;
+ imgtoolerr_t err = IMGTOOLERR_SUCCESS;
+
+
+ assert(fileref->l2_img->format == L2I_MFS);
+
+ /* run through link chain until we reach the old or the new EOF */
+ AB_link = fileref->u.mfs.stBlk;
+ if ((AB_link == 0) || (AB_link >= fileref->l2_img->numABs+2))
+ /* 0 -> ??? */
+ return IMGTOOLERR_CORRUPTIMAGE;
+ for (i=0; (i<newABeof) && (AB_link!=1); i++)
+ {
+ AB_address = AB_link - 2;
+ AB_link = mfs_get_ABlink(fileref->l2_img, AB_address);
+ if ((AB_link == 0) || (AB_link >= fileref->l2_img->numABs+2))
+ /* 0 -> empty block: there is no way an empty block could make it
+ into the link chain!!! */
+ return IMGTOOLERR_CORRUPTIMAGE;
+
+ if (TAG_CHECKS)
+ {
+ /* optional check */
+ if (image_get_tag_len(&fileref->l2_img->l1_img) == 12)
+ {
+ for (j=0; j<fileref->l2_img->blocksperAB; j++)
+ {
+ err = image_read_tag(&fileref->l2_img->l1_img, fileref->l2_img->u.mfs.ABStart + AB_address * fileref->l2_img->blocksperAB + j, &tag);
+ if (err)
+ return err;
+
+ if ((get_UINT32BE(tag.fileID) != fileref->fileID)
+ || (((tag.ftype & 2) != 0) != (fileref->forkType == rsrc_fork))
+ || (get_UINT16BE(tag.fblock) != ((i * fileref->l2_img->blocksperAB + j) & 0xffff)))
+ {
+ return IMGTOOLERR_CORRUPTIMAGE;
+ }
+ }
+ }
+ }
+ }
+
+ if (i == newABeof)
+ { /* new EOF is shorter than old one */
+ /* mark new eof */
+ if (i==0)
+ fileref->u.mfs.stBlk = 1;
+ else
+ {
+ mfs_set_ABlink(fileref->l2_img, AB_address, 1);
+ MDB_dirty = 1;
+ }
+
+ /* free all remaining blocks */
+ while (AB_link != 1)
+ {
+ AB_address = AB_link - 2;
+ AB_link = mfs_get_ABlink(fileref->l2_img, AB_address);
+ if ((AB_link == 0) || (AB_link >= fileref->l2_img->numABs+2))
+ { /* 0 -> empty block: there is no way an empty block could make
+ it into the link chain!!! */
+ if (MDB_dirty)
+ { /* update MDB (freeABs field) and ABLink array */
+ err = mfs_update_mdb(fileref->l2_img);
+ if (err)
+ return err;
+ }
+ return IMGTOOLERR_CORRUPTIMAGE;
+ }
+
+ if (TAG_CHECKS)
+ {
+ /* optional check */
+ if (image_get_tag_len(&fileref->l2_img->l1_img) == 12)
+ {
+ for (j=0; j<fileref->l2_img->blocksperAB; j++)
+ {
+ err = image_read_tag(&fileref->l2_img->l1_img, fileref->l2_img->u.mfs.ABStart + AB_address * fileref->l2_img->blocksperAB + j, &tag);
+ if (err)
+ return err;
+
+ if ((get_UINT32BE(tag.fileID) != fileref->fileID)
+ || (((tag.ftype & 2) != 0) != (fileref->forkType == rsrc_fork))
+ || (get_UINT16BE(tag.fblock) != ((i * fileref->l2_img->blocksperAB + j) & 0xffff)))
+ {
+ return IMGTOOLERR_CORRUPTIMAGE;
+ }
+ }
+ }
+ }
+
+ mfs_set_ABlink(fileref->l2_img, AB_address, 0);
+ fileref->l2_img->freeABs++;
+ MDB_dirty = 1;
+ /* set tag to free */
+ if (image_get_tag_len(&fileref->l2_img->l1_img) == 12)
+ {
+ memset(&tag, 0, sizeof(tag));
+ for (j=0; j<fileref->l2_img->blocksperAB; j++)
+ {
+ err = image_write_tag(&fileref->l2_img->l1_img, fileref->l2_img->u.mfs.ABStart + AB_address * fileref->l2_img->blocksperAB + j, &tag);
+ if (err)
+ return err;
+ }
+ }
+ i++;
+ }
+ }
+ else
+ { /* new EOF is larger than old one */
+ err = mfs_file_allocABs(fileref, AB_address, newABeof - i, i * fileref->l2_img->blocksperAB);
+ if (err)
+ return err;
+ MDB_dirty = 1;
+ }
+
+ if (MDB_dirty)
+ { /* update MDB (freeABs field) and ABLink array */
+ err = mfs_update_mdb(fileref->l2_img);
+ if (err)
+ return err;
+ }
+
+ fileref->pLen = newABeof * (fileref->l2_img->blocksperAB * 512);
+
+ return IMGTOOLERR_SUCCESS;
+}
+
+#ifdef UNUSED_FUNCTION
+/*
+ mfs_hashString
+
+ Hash a string: under MFS, this provides the resource ID of the comment
+ resource associated with the file whose name is provided (FCMT resource
+ type).
+
+ Ripped from Apple technote TB06 (converted from 68k ASM to C)
+
+ string (I): string to hash
+
+ Returns hash value
+*/
+static int mfs_hashString(const mac_str255 string)
+{
+ int reply;
+ int len;
+ int i;
+
+ len = string[0];
+
+ reply = 0;
+ for (i=0; i<len; i++)
+ {
+ reply ^= string[i+1];
+ if (reply & 1)
+ reply = ((reply >> 1) & 0x7fff) | ~0x7fff;
+ else
+ reply = ((reply >> 1) & 0x7fff);
+ if (! (reply & 0x8000))
+ reply = - reply;
+ }
+
+ return reply;
+}
+#endif
+
+#if 0
+#pragma mark -
+#pragma mark HFS IMPLEMENTATION
+#endif
+
+/*
+ HFS extents B-tree key
+*/
+typedef struct hfs_extentKey
+{
+ UINT8 keyLength; /* length of key, excluding this field */
+ UINT8 forkType; /* 0 = data fork, FF = resource fork */
+ UINT32BE fileID; /* file ID */
+ UINT16BE startBlock; /* first file allocation block number in this extent */
+} hfs_extentKey;
+enum
+{
+ keyLength_hfs_extentKey = sizeof(hfs_extentKey) - sizeof(UINT8)
+};
+
+/*
+ HFS catalog B-tree key
+*/
+typedef struct hfs_catKey
+{
+ UINT8 keyLen; /* key length */
+ UINT8 resrv1; /* reserved */
+ UINT32BE parID; /* parent directory ID */
+ mac_str31 cName; /* catalog node name */
+ /* note that in index nodes, it is a mac_str31, but
+ in leaf keys it's a variable-length string */
+} hfs_catKey;
+
+/*
+ HFS catalog data record for a folder - 70 bytes
+*/
+typedef struct hfs_catFolderData
+{
+ UINT16BE recordType; /* record type */
+ UINT16BE flags; /* folder flags */
+ UINT16BE valence; /* folder valence */
+ UINT32BE folderID; /* folder ID */
+ UINT32BE createDate; /* date and time of creation */
+ UINT32BE modifyDate; /* date and time of last modification */
+ UINT32BE backupDate; /* date and time of last backup */
+ mac_DInfo userInfo; /* Finder information */
+ mac_DXInfo finderInfo; /* additional Finder information */
+ UINT32BE reserved[4]; /* reserved - set to zero */
+} hfs_catFolderData;
+
+/*
+ HFS catalog data record for a file - 102 bytes
+*/
+typedef struct hfs_catFileData
+{
+ UINT16BE recordType; /* record type */
+ UINT8 flags; /* file flags */
+ UINT8 fileType; /* file type (reserved, always 0?) */
+ mac_FInfo userInfo; /* Finder information */
+ UINT32BE fileID; /* file ID */
+ UINT16BE dataStartBlock; /* not used - set to zero */
+ UINT32BE dataLogicalSize; /* logical EOF of data fork */
+ UINT32BE dataPhysicalSize; /* physical EOF of data fork */
+ UINT16BE rsrcStartBlock; /* not used - set to zero */
+ UINT32BE rsrcLogicalSize; /* logical EOF of resource fork */
+ UINT32BE rsrcPhysicalSize; /* physical EOF of resource fork */
+ UINT32BE createDate; /* date and time of creation */
+ UINT32BE modifyDate; /* date and time of last modification */
+ UINT32BE backupDate; /* date and time of last backup */
+ mac_FXInfo finderInfo; /* additional Finder information */
+ UINT16BE clumpSize; /* file clump size (not used) */
+ hfs_extent_3 dataExtents; /* first data fork extent record */
+ hfs_extent_3 rsrcExtents; /* first resource fork extent record */
+ UINT32BE reserved; /* reserved - set to zero */
+} hfs_catFileData;
+
+/*
+ HFS catalog data record for a thread - 46 bytes
+
+ The key for a thread record features the CNID of the item and an empty
+ name, instead of the CNID of the parent and the item name.
+*/
+typedef struct hfs_catThreadData
+{
+ UINT16BE recordType; /* record type */
+ UINT32BE reserved[2]; /* reserved - set to zero */
+ UINT32BE parID; /* parent ID for this catalog node */
+ mac_str31 nodeName; /* name of this catalog node */
+} hfs_catThreadData;
+
+/*
+ union for all types at once
+*/
+typedef union hfs_catData
+{
+ UINT16BE dataType;
+ hfs_catFolderData folder;
+ hfs_catFileData file;
+ hfs_catThreadData thread;
+} hfs_catData;
+
+/*
+ HFS catalog record types
+*/
+enum
+{
+ hcrt_Folder = 0x0100, /* Folder record */
+ hcrt_File = 0x0200, /* File record */
+ hcrt_FolderThread = 0x0300, /* Folder thread record */
+ hcrt_FileThread = 0x0400 /* File thread record */
+};
+
+/*
+ Catalog file record flags
+
+ This is similar to the MFS catalog flag field, but the "thread exists" flag
+ (0x02) is specific to HFS/HFS+, whereas the "Record in use" flag (0x80) is
+ only used by MFS.
+*/
+enum
+{
+ cfrf_fileLocked = 0x01, /* file is locked and cannot be written to */
+ cfrf_threadExists = 0x02 /* a file thread record exists for this file */
+};
+
+/*
+ BT functions used by HFS functions
+*/
+typedef struct BT_leaf_rec_enumerator
+{
+ mac_BTref *BTref;
+ UINT32 cur_node;
+ int cur_rec;
+} BT_leaf_rec_enumerator;
+
+static imgtoolerr_t BT_open(mac_BTref *BTref, int (*key_compare_func)(const void *key1, const void *key2), int is_extent);
+static void BT_close(mac_BTref *BTref);
+static imgtoolerr_t BT_search_leaf_rec(mac_BTref *BTref, const void *search_key,
+ UINT32 *node_ID, int *record_ID,
+ void **record_ptr, int *record_len,
+ int search_exact_match, int *match_found);
+static imgtoolerr_t BT_get_keyed_record_data(mac_BTref *BTref, void *rec_ptr, int rec_len, void **data_ptr, int *data_len);
+static imgtoolerr_t BT_leaf_rec_enumerator_open(mac_BTref *BTref, BT_leaf_rec_enumerator *enumerator);
+static imgtoolerr_t BT_leaf_rec_enumerator_read(BT_leaf_rec_enumerator *enumerator, void **record_ptr, int *rec_len);
+
+typedef struct hfs_cat_enumerator
+{
+ mac_l2_imgref *l2_img;
+ BT_leaf_rec_enumerator BT_enumerator;
+ UINT32 parID;
+} hfs_cat_enumerator;
+
+/*
+ hfs_open_extents_file
+
+ Open the file extents B-tree file
+
+ l2_img (I/O): level-2 image reference
+ mdb (I): copy of the MDB block
+ fileref (O): mac open file reference
+
+ Return imgtool error code
+*/
+static imgtoolerr_t hfs_open_extents_file(mac_l2_imgref *l2_img, const hfs_mdb_t *mdb, mac_fileref *fileref)
+{
+ assert(l2_img->format == L2I_HFS);
+
+ fileref->l2_img = l2_img;
+
+ fileref->fileID = 3;
+ fileref->forkType = (mac_forkID)0x00;
+
+ fileref->eof = fileref->pLen = get_UINT32BE(mdb->xtFlSize);
+ memcpy(fileref->u.hfs.extents, mdb->xtExtRec, sizeof(hfs_extent_3));
+
+ fileref->crPs = 0;
+
+ fileref->reload_buf = TRUE;
+
+ return IMGTOOLERR_SUCCESS;
+}
+
+/*
+ hfs_open_cat_file
+
+ Open the disk catalog B-tree file
+
+ l2_img (I/O): level-2 image reference
+ mdb (I): copy of the MDB block
+ fileref (O): mac open file reference
+
+ Return imgtool error code
+*/
+static imgtoolerr_t hfs_open_cat_file(mac_l2_imgref *l2_img, const hfs_mdb_t *mdb, mac_fileref *fileref)
+{
+ assert(l2_img->format == L2I_HFS);
+
+ fileref->l2_img = l2_img;
+
+ fileref->fileID = 4;
+ fileref->forkType = (mac_forkID)0x00;
+
+ fileref->eof = fileref->pLen = get_UINT32BE(mdb->ctFlSize);
+ memcpy(fileref->u.hfs.extents, mdb->ctExtRec, sizeof(hfs_extent_3));
+
+ fileref->crPs = 0;
+
+ fileref->reload_buf = TRUE;
+
+ return IMGTOOLERR_SUCCESS;
+}
+
+/*
+ hfs_extentKey_compare
+
+ key compare function for file extents B-tree
+
+ p1 (I): pointer to first key
+ p2 (I): pointer to second key
+
+ Return a zero the two keys are equal, a negative value if the key pointed
+ to by p1 is less than the key pointed to by p2, and a positive value if the
+ key pointed to by p1 is greater than the key pointed to by p2.
+*/
+static int hfs_extentKey_compare(const void *p1, const void *p2)
+{
+ const hfs_extentKey *key1 = (const hfs_extentKey*)p1;
+ const hfs_extentKey *key2 = (const hfs_extentKey*)p2;
+
+ /* let's keep it simple for now */
+ return memcmp(key1, key2, sizeof(hfs_extentKey));
+}
+
+/*
+ hfs_catKey_compare
+
+ key compare function for disk catalog B-tree
+
+ p1 (I): pointer to first key
+ p2 (I): pointer to second key
+
+ Return a zero the two keys are equal, a negative value if the key pointed
+ to by p1 is less than the key pointed to by p2, and a positive value if the
+ key pointed to by p1 is greater than the key pointed to by p2.
+*/
+static int hfs_catKey_compare(const void *p1, const void *p2)
+{
+ const hfs_catKey *key1 = (const hfs_catKey *)p1;
+ const hfs_catKey *key2 = (const hfs_catKey *)p2;
+
+ if (get_UINT32BE(key1->parID) != get_UINT32BE(key2->parID))
+ return (get_UINT32BE(key1->parID) < get_UINT32BE(key2->parID)) ? -1 : +1;
+
+ return mac_stricmp(key1->cName, key2->cName);
+}
+
+/*
+ hfs_image_open
+
+ Open a HFS image. Image must already be open on level 1.
+
+ l2_img (I/O): level-2 image reference to open (l1_img and format fields
+ must be initialized)
+ img_open_buf (I): buffer with the MDB block
+
+ Return imgtool error code
+*/
+static imgtoolerr_t hfs_image_open(imgtool_image *image, imgtool_stream *stream)
+{
+ imgtoolerr_t err;
+ mac_l2_imgref *l2_img;
+ img_open_buf buf_local;
+ img_open_buf *buf;
+
+ l2_img = get_imgref(image);
+ l2_img->l1_img.image = image;
+ l2_img->l1_img.heads = 2;
+ l2_img->format = L2I_HFS;
+
+ /* read MDB */
+ err = image_read_block(&l2_img->l1_img, 2, &buf_local.raw);
+ if (err)
+ return err;
+ buf = &buf_local;
+
+ /* check signature word */
+ if ((buf->hfs_mdb.sigWord[0] != 0x42) || (buf->hfs_mdb.sigWord[1] != 0x44)
+ || (buf->hfs_mdb.VN[0] > 27))
+ return IMGTOOLERR_CORRUPTIMAGE;
+
+ l2_img->u.hfs.VBM_start = get_UINT16BE(buf->hfs_mdb.VBMSt);
+
+ l2_img->numABs = get_UINT16BE(buf->hfs_mdb.nmAlBlks);
+ if (get_UINT32BE(buf->hfs_mdb.alBlkSiz) % 512)
+ return IMGTOOLERR_CORRUPTIMAGE;
+ l2_img->blocksperAB = get_UINT32BE(buf->hfs_mdb.alBlkSiz) / 512;
+ l2_img->u.hfs.ABStart = get_UINT16BE(buf->hfs_mdb.alBlSt);
+
+ l2_img->nxtCNID = get_UINT32BE(buf->hfs_mdb.nxtCNID);
+
+ l2_img->freeABs = get_UINT16BE(buf->hfs_mdb.freeABs);
+
+ mac_strcpy(l2_img->u.hfs.volname, buf->hfs_mdb.VN);
+
+ /* open extents and catalog BT */
+ err = hfs_open_extents_file(l2_img, &buf->hfs_mdb, &l2_img->u.hfs.extents_BT.fileref);
+ if (err)
+ return err;
+ err = BT_open(&l2_img->u.hfs.extents_BT, hfs_extentKey_compare, TRUE);
+ if (err)
+ return err;
+ if ((l2_img->u.hfs.extents_BT.attributes & btha_bigKeysMask)
+ /*|| (l2_img->u.hfs.extents_BT.attributes & kBTVariableIndexKeysMask)*/
+ || (l2_img->u.hfs.extents_BT.maxKeyLength != 7))
+ { /* This is not supported by the HFS format */
+ /* Variable Index keys are not supported either, but hopefully it will
+ not break this imgtool module if it set (though it would probably break
+ a real macintosh) */
+ BT_close(&l2_img->u.hfs.extents_BT);
+ return IMGTOOLERR_CORRUPTIMAGE;
+ }
+ err = hfs_open_cat_file(l2_img, &buf->hfs_mdb, &l2_img->u.hfs.cat_BT.fileref);
+ if (err)
+ {
+ BT_close(&l2_img->u.hfs.extents_BT);
+ return err;
+ }
+ err = BT_open(&l2_img->u.hfs.cat_BT, hfs_catKey_compare, FALSE);
+ if (err)
+ {
+ return err;
+ }
+ if ((l2_img->u.hfs.cat_BT.attributes & btha_bigKeysMask)
+ /*|| (l2_img->u.hfs.cat_BT.attributes & kBTVariableIndexKeysMask)*/
+ || (l2_img->u.hfs.cat_BT.maxKeyLength != 37))
+ { /* This is not supported by the HFS format */
+ /* Variable Index keys are not supported either, but hopefully it will
+ not break this imgtool module if it set (though it would probably break
+ a real macintosh) */
+ BT_close(&l2_img->u.hfs.extents_BT);
+ BT_close(&l2_img->u.hfs.cat_BT);
+ return IMGTOOLERR_CORRUPTIMAGE;
+ }
+
+ /* extract volume bitmap */
+ {
+ int byte_len = (l2_img->numABs + 7) / 8;
+ int cur_byte = 0;
+ int cur_block = l2_img->u.hfs.VBM_start;
+
+ while (cur_byte < byte_len)
+ {
+ /* read next block */
+ err = image_read_block(&l2_img->l1_img, cur_block, buf->raw);
+ if (err)
+ return err;
+ cur_block++;
+
+ /* append this block to VBM */
+ memcpy(l2_img->u.hfs.VBM+cur_byte, buf->raw, 512);
+ cur_byte += 512;
+ }
+ }
+
+ return IMGTOOLERR_SUCCESS;
+}
+
+#ifdef UNUSED_FUNCTION
+/*
+ hfs_image_close
+
+ Close a HFS image.
+
+ l2_img (I/O): level-2 image reference
+*/
+static void hfs_image_close(mac_l2_imgref *l2_img)
+{
+ assert(l2_img->format == L2I_HFS);
+
+ BT_close(&l2_img->u.hfs.extents_BT);
+ BT_close(&l2_img->u.hfs.cat_BT);
+}
+#endif
+
+/*
+ hfs_get_cat_record_data
+
+ extract data from a catalog B-tree leaf record
+
+ l2_img (I/O): level-2 image reference
+ rec_raw (I): pointer to record key and data, as returned by
+ BT_node_get_keyed_record
+ rec_len (I): total length of record, as returned by
+ BT_node_get_keyed_record
+ rec_key (O): set to point to record key
+ rec_data (O): set to point to record data
+
+ Return imgtool error code
+*/
+static imgtoolerr_t hfs_get_cat_record_data(mac_l2_imgref *l2_img, void *rec_raw, int rec_len, hfs_catKey **rec_key, hfs_catData **rec_data)
+{
+ hfs_catKey *lrec_key;
+ void *rec_data_raw;
+ hfs_catData *lrec_data;
+ int rec_data_len;
+ int min_data_size;
+ imgtoolerr_t err;
+
+
+ assert(l2_img->format == L2I_HFS);
+
+ lrec_key = (hfs_catKey*)rec_raw;
+ /* check that key is long enough to hold it all */
+ if ((lrec_key->keyLen+1) < (offsetof(hfs_catKey, cName) + lrec_key->cName[0] + 1))
+ return IMGTOOLERR_CORRUPTIMAGE;
+
+ /* get pointer to record data */
+ err = BT_get_keyed_record_data(&l2_img->u.hfs.cat_BT, rec_raw, rec_len, &rec_data_raw, &rec_data_len);
+ if (err)
+ return err;
+ lrec_data = (hfs_catData*)rec_data_raw;
+
+ /* extract record type */
+ if (rec_data_len < 2)
+ return IMGTOOLERR_CORRUPTIMAGE;
+
+ /* check that the record is large enough for its type */
+ switch (get_UINT16BE(lrec_data->dataType))
+ {
+ case hcrt_Folder:
+ min_data_size = sizeof(hfs_catFolderData);
+ break;
+
+ case hcrt_File:
+ min_data_size = sizeof(hfs_catFileData);
+ break;
+
+ case hcrt_FolderThread:
+ case hcrt_FileThread:
+ min_data_size = sizeof(hfs_catThreadData);
+ break;
+
+ default:
+ /* records of unknown type can be safely ignored */
+ min_data_size = 0;
+ break;
+ }
+
+ if (rec_data_len < min_data_size)
+ return IMGTOOLERR_CORRUPTIMAGE;
+
+ if (rec_key)
+ *rec_key = lrec_key;
+ if (rec_data)
+ *rec_data = lrec_data;
+
+ return IMGTOOLERR_SUCCESS;
+}
+
+/*
+ hfs_cat_open
+
+ Open an enumerator on the disk catalog
+
+ l2_img (I/O): level-2 image reference
+ enumerator (O): open catalog enumerator reference
+
+ Return imgtool error code
+*/
+static imgtoolerr_t hfs_cat_open(mac_l2_imgref *l2_img, const char *path, hfs_cat_enumerator *enumerator)
+{
+ imgtoolerr_t err;
+ UINT32 parID;
+ mac_str255 filename;
+ mac_dirent cat_info;
+
+ assert(l2_img->format == L2I_HFS);
+
+ /* resolve path and fetch file info from directory/catalog */
+ err = mac_lookup_path(l2_img, path, &parID, filename, &cat_info, FALSE);
+ if (err)
+ return err;
+ if (cat_info.dataRecType != hcrt_Folder)
+ return IMGTOOLERR_FILENOTFOUND;
+
+ enumerator->l2_img = l2_img;
+ enumerator->parID = parID;
+
+ return BT_leaf_rec_enumerator_open(&l2_img->u.hfs.cat_BT, &enumerator->BT_enumerator);
+}
+
+/*
+ hfs_cat_read
+
+ Enumerate the disk catalog
+
+ enumerator (I/O): open catalog enumerator reference
+ rec_key (O): set to point to record key
+ rec_data (O): set to point to record data
+
+ Return imgtool error code
+*/
+static imgtoolerr_t hfs_cat_read(hfs_cat_enumerator *enumerator, hfs_catKey **rec_key, hfs_catData **rec_data)
+{
+ void *rec;
+ int rec_len = 0;
+ imgtoolerr_t err;
+
+
+ *rec_key = NULL;
+ *rec_data = NULL;
+
+ /* read next record */
+ err = BT_leaf_rec_enumerator_read(&enumerator->BT_enumerator, &rec, &rec_len);
+ if (err)
+ return err;
+
+ /* check EOList condition */
+ if (rec == NULL)
+ return IMGTOOLERR_SUCCESS;
+
+ /* extract record data */
+ err = hfs_get_cat_record_data(enumerator->l2_img, rec, rec_len, rec_key, rec_data);
+ if (err)
+ return err;
+
+ return IMGTOOLERR_SUCCESS;
+}
+
+/*
+ hfs_cat_search
+
+ Search the catalog for a given file
+
+ l2_img (I/O): level-2 image reference
+ parID (I): CNID of file parent directory
+ cName (I): file name
+ rec_key (O): set to point to record key
+ rec_data (O): set to point to record data
+
+ Return imgtool error code
+*/
+static imgtoolerr_t hfs_cat_search(mac_l2_imgref *l2_img, UINT32 parID, const mac_str31 cName, hfs_catKey **rec_key, hfs_catData **rec_data)
+{
+ hfs_catKey search_key;
+ void *rec;
+ int rec_len;
+ imgtoolerr_t err;
+
+ assert(l2_img->format == L2I_HFS);
+
+ if (cName[0] > 31)
+ return IMGTOOLERR_UNEXPECTED;
+
+ /* generate search key */
+ search_key.keyLen = search_key.resrv1 = 0; /* these fields do not matter
+ to the compare function, so we
+ don't fill them */
+ set_UINT32BE(&search_key.parID, parID);
+ mac_strcpy(search_key.cName, cName);
+
+ /* search key */
+ err = BT_search_leaf_rec(&l2_img->u.hfs.cat_BT, &search_key, NULL, NULL, &rec, &rec_len, TRUE, NULL);
+ if (err)
+ return err;
+
+ /* extract record data */
+ err = hfs_get_cat_record_data(l2_img, rec, rec_len, rec_key, rec_data);
+ if (err)
+ return err;
+
+ return IMGTOOLERR_SUCCESS;
+}
+
+/*
+ hfs_lookup_path
+
+ Resolve a file path
+
+ l2_img (I/O): level-2 image reference
+ fpath (I): file path (C string)
+ parID (O): set to the CNID of the file parent directory
+ filename (O): set to the actual name of the file, with capitalization matching
+ the one on the volume rather than the one in the fpath parameter (Mac
+ string)
+ cat_info (O): catalog info for this file extracted from the catalog file
+ (may be NULL)
+
+ Return imgtool error code
+*/
+static imgtoolerr_t hfs_lookup_path(mac_l2_imgref *l2_img, const char *fpath, UINT32 *parID, mac_str255 filename, mac_dirent *cat_info)
+{
+ const char *element_start;
+ int element_len;
+ mac_str255 mac_element_name;
+ //int level;
+ imgtoolerr_t err;
+ hfs_catKey *catrec_key = NULL;
+ hfs_catData *catrec_data = NULL;
+ UINT16 dataRecType = hcrt_Folder;
+
+ /* iterate each path element */
+ element_start = fpath;
+ //level = 0;
+ *parID = 2; /* root parID is 2 */
+
+ while(*element_start)
+ {
+ /* find next path element */
+ element_len = strlen(element_start);
+ /* decode path element name */
+ c_to_mac_strncpy(mac_element_name, element_start, element_len);
+
+ err = hfs_cat_search(l2_img, *parID, mac_element_name, &catrec_key, &catrec_data);
+ if (err)
+ return err;
+
+ dataRecType = get_UINT16BE(catrec_data->dataType);
+
+ /* regular folder/file name */
+ if (dataRecType == hcrt_Folder)
+ *parID = get_UINT32BE(catrec_data->folder.folderID);
+ else if (element_start[element_len + 1])
+ return IMGTOOLERR_BADFILENAME;
+
+ /* iterate */
+ element_start += element_len + 1;
+ }
+
+ if (catrec_key && (dataRecType == hcrt_File))
+ {
+ /* save ref */
+ *parID = get_UINT32BE(catrec_key->parID);
+ mac_strcpy(filename, catrec_key->cName);
+ }
+
+ if (cat_info)
+ {
+ if (catrec_data && (dataRecType == hcrt_File))
+ {
+ cat_info->flFinderInfo = catrec_data->file.userInfo;
+ cat_info->flXFinderInfo = catrec_data->file.finderInfo;
+ cat_info->flags = catrec_data->file.flags;
+ cat_info->fileID = get_UINT32BE(catrec_data->file.fileID);
+ cat_info->dataLogicalSize = get_UINT32BE(catrec_data->file.dataLogicalSize);
+ cat_info->dataPhysicalSize = get_UINT32BE(catrec_data->file.dataPhysicalSize);
+ cat_info->rsrcLogicalSize = get_UINT32BE(catrec_data->file.rsrcLogicalSize);
+ cat_info->rsrcPhysicalSize = get_UINT32BE(catrec_data->file.rsrcPhysicalSize);
+ cat_info->createDate = get_UINT32BE(catrec_data->file.createDate);
+ cat_info->modifyDate = get_UINT32BE(catrec_data->file.modifyDate);
+ }
+ else
+ {
+ memset(cat_info, 0, sizeof(*cat_info));
+ }
+ cat_info->dataRecType = dataRecType;
+ }
+
+ return IMGTOOLERR_SUCCESS;
+}
+
+/*
+ hfs_file_open_internal
+
+ Open a file fork, given its catalog entry. This function should not be
+ called directly: call hfs_file_open instead.
+
+ l2_img (I/O): level-2 image reference
+ file_rec (I): catalog entry for the file to open
+ mac_forkID (I): tells which fork should be opened
+ fileref (O): mac file reference to open
+
+ Return imgtool error code
+*/
+static imgtoolerr_t hfs_file_open_internal(mac_l2_imgref *l2_img, const hfs_catFileData *file_rec, mac_forkID fork, mac_fileref *fileref)
+{
+ assert(l2_img->format == L2I_HFS);
+
+ fileref->l2_img = l2_img;
+
+ fileref->fileID = get_UINT32BE(file_rec->fileID);
+ fileref->forkType = fork;
+
+ switch (fork)
+ {
+ case data_fork:
+ fileref->eof = get_UINT32BE(file_rec->dataLogicalSize);
+ fileref->pLen = get_UINT32BE(file_rec->dataPhysicalSize);
+ memcpy(fileref->u.hfs.extents, file_rec->dataExtents, sizeof(hfs_extent_3));
+ break;
+
+ case rsrc_fork:
+ fileref->eof = get_UINT32BE(file_rec->rsrcLogicalSize);
+ fileref->pLen = get_UINT32BE(file_rec->rsrcPhysicalSize);
+ memcpy(fileref->u.hfs.extents, file_rec->rsrcExtents, sizeof(hfs_extent_3));
+ break;
+ }
+
+ fileref->crPs = 0;
+
+ fileref->reload_buf = TRUE;
+
+ return IMGTOOLERR_SUCCESS;
+}
+
+/*
+ hfs_file_open
+
+ Open a file located on a HFS volume. This function should not be called
+ directly: call mac_file_open instead.
+
+ l2_img (I/O): level-2 image reference
+ parID (I): CNID of file parent directory
+ filename (I): name of the file (Mac string)
+ mac_forkID (I): tells which fork should be opened
+ fileref (O): mac file reference to open
+
+ Return imgtool error code
+*/
+static imgtoolerr_t hfs_file_open(mac_l2_imgref *l2_img, UINT32 parID, const mac_str255 filename, mac_forkID fork, mac_fileref *fileref)
+{
+ hfs_catKey *catrec_key;
+ hfs_catData *catrec_data;
+ UINT16 dataRecType;
+ imgtoolerr_t err;
+
+ /* lookup file in catalog */
+ err = hfs_cat_search(l2_img, parID, filename, &catrec_key, &catrec_data);
+ if (err)
+ return err;
+
+ dataRecType = get_UINT16BE(catrec_data->dataType);
+
+ /* file expected */
+ if (dataRecType != hcrt_File)
+ return IMGTOOLERR_BADFILENAME;
+
+ fileref->u.hfs.parID = get_UINT32BE(catrec_key->parID);
+ mac_strcpy(fileref->u.hfs.filename, catrec_key->cName);
+
+ /* open it */
+ return hfs_file_open_internal(l2_img, &catrec_data->file, fork, fileref);
+}
+
+/*
+ hfs_file_get_nth_block_address
+
+ Get the disk block address of a given block in an open file on a MFS image.
+ Called by macintosh file code.
+
+ fileref (I/O): open mac file reference
+ block_num (I): file block index
+ block_address (O): disk block address for the file block
+
+ Return imgtool error code
+*/
+static imgtoolerr_t hfs_file_get_nth_block_address(mac_fileref *fileref, UINT32 block_num, UINT32 *block_address)
+{
+ UINT32 AB_num;
+ UINT32 cur_AB;
+ UINT32 i;
+ void *cur_extents_raw;
+ hfs_extent *cur_extents;
+ int cur_extents_len;
+ void *extents_BT_rec;
+ int extents_BT_rec_len;
+ imgtoolerr_t err;
+ UINT16 AB_address;
+
+ assert(fileref->l2_img->format == L2I_HFS);
+
+ AB_num = block_num / fileref->l2_img->blocksperAB;
+ cur_AB = 0;
+ cur_extents = fileref->u.hfs.extents;
+
+ /* first look in catalog tree extents */
+ for (i=0; i<3; i++)
+ {
+ if (AB_num < cur_AB+get_UINT16BE(cur_extents[i].numABlks))
+ break;
+ cur_AB += get_UINT16BE(cur_extents[i].numABlks);
+ }
+ if (i == 3)
+ {
+ /* extent not found: read extents record from extents BT */
+ hfs_extentKey search_key;
+ hfs_extentKey *found_key;
+
+ search_key.keyLength = keyLength_hfs_extentKey;
+ search_key.forkType = fileref->forkType;
+ set_UINT32BE(&search_key.fileID, fileref->fileID);
+ set_UINT16BE(&search_key.startBlock, AB_num);
+
+ /* search for the record with the largest key lower than or equal to
+ search_key. The keys are constructed in such a way that, if a record
+ includes AB_num, it is that one. */
+ err = BT_search_leaf_rec(&fileref->l2_img->u.hfs.extents_BT, &search_key,
+ NULL, NULL, &extents_BT_rec, &extents_BT_rec_len,
+ FALSE, NULL);
+ if (err)
+ return err;
+
+ if (extents_BT_rec == NULL)
+ return IMGTOOLERR_CORRUPTIMAGE;
+
+ found_key = (hfs_extentKey*)extents_BT_rec;
+ /* check that this record concerns the correct file */
+ if ((found_key->forkType != fileref->forkType)
+ || (get_UINT32BE(found_key->fileID) != fileref->fileID))
+ return IMGTOOLERR_CORRUPTIMAGE;
+
+ /* extract start AB */
+ cur_AB = get_UINT16BE(found_key->startBlock);
+ /* get extents pointer */
+ err = BT_get_keyed_record_data(&fileref->l2_img->u.hfs.extents_BT, extents_BT_rec, extents_BT_rec_len, &cur_extents_raw, &cur_extents_len);
+ if (err)
+ return err;
+ if (cur_extents_len < 3*sizeof(hfs_extent))
+ return IMGTOOLERR_CORRUPTIMAGE;
+ cur_extents = (hfs_extent*)cur_extents_raw;
+
+ /* pick correct extent in record */
+ for (i=0; i<3; i++)
+ {
+ if (AB_num < cur_AB+get_UINT16BE(cur_extents[i].numABlks))
+ break;
+ cur_AB += get_UINT16BE(cur_extents[i].numABlks);
+ }
+ if (i == 3)
+ /* extent not found */
+ return IMGTOOLERR_CORRUPTIMAGE;
+ }
+
+ AB_address = get_UINT16BE(cur_extents[i].stABN) + (AB_num-cur_AB);
+
+ if (AB_address >= fileref->l2_img->numABs)
+ return IMGTOOLERR_CORRUPTIMAGE;
+
+ *block_address = fileref->l2_img->u.hfs.ABStart + AB_address * fileref->l2_img->blocksperAB
+ + block_num % fileref->l2_img->blocksperAB;
+
+ return IMGTOOLERR_SUCCESS;
+}
+
+#if 0
+#pragma mark -
+#pragma mark B-TREE IMPLEMENTATION
+#endif
+
+/*
+ B-tree (Balanced search tree) files are used by the HFS and HFS+ file
+ systems: the Extents and Catalog files are both B-Tree.
+
+ Note that these B-trees are B+-trees: data is only on the leaf level, and
+ nodes located on the same level are also linked sequentially, which allows
+ fast sequenctial access to the catalog file.
+
+ These files are normal files, except for the fact that they are not
+ referenced from the catalog but the MDB. They are allocated in fixed-size
+ records of 512 bytes (HFS). (HFS+ supports any power of two from 512
+ through 32768, and uses a default of 1024 for Extents, and 4096 for both
+ Catalog and Attributes.)
+
+ Nodes can contain any number of records. The nodes can be of any of four
+ types: header node (unique node with b-tree information, pointer to root
+ node and start of the node allocation bitmap), map nodes (which are created
+ when the node allocation bitmap outgrows the header node), index nodes
+ (root and branch node that enable to efficiently search the leaf nodes for
+ a specific key value), and leaf nodes (which hold the actual user data
+ records with keys and data). The first node is always a header node.
+ Other nodes can be of any of the 3 other type, or they can be free.
+*/
+
+/*
+ BTNodeHeader
+
+ Header of a node record
+*/
+typedef struct BTNodeHeader
+{
+ UINT32BE fLink; /* (index of) next node at this level */
+ UINT32BE bLink; /* (index of) previous node at this level */
+ UINT8 kind; /* kind of node (leaf, index, header, map) */
+ UINT8 height; /* zero for header, map; 1 for leaf, 2 through
+ treeDepth for index (child is one LESS than
+ parent, whatever IM says) */
+ UINT16BE numRecords; /* number of records in this node */
+ UINT16BE reserved; /* reserved; set to zero */
+} BTNodeHeader;
+
+/*
+ Constants for BTNodeHeader kind field
+*/
+enum
+{
+ btnk_leafNode = 0xff, /* leaf nodes hold the actual user data records
+ with keys and data */
+ btnk_indexNode = 0, /* root and branch node that enable to efficiently
+ search the leaf nodes for a specific key value */
+ btnk_headerNode = 1, /* unique node with b-tree information, pointer to
+ root node and start of the node allocation
+ bitmap */
+ btnk_mapNode = 2 /* map nodes are created when the node allocation
+ bitmap outgrows the header node */
+};
+
+/*
+ BTHeaderRecord: first record of a B-tree header node (second record is
+ unused, and third is node allocation bitmap).
+*/
+typedef struct BTHeaderRecord
+{
+ UINT16BE treeDepth; /* maximum height (usually leaf nodes) */
+ UINT32BE rootNode; /* node number of root node */
+ UINT32BE leafRecords; /* number of leaf records in all leaf nodes */
+ UINT32BE firstLeafNode; /* node number of first leaf node */
+ UINT32BE lastLeafNode; /* node number of last leaf node */
+ UINT16BE nodeSize; /* size of a node, in bytes */
+ UINT16BE maxKeyLength; /* maximum length of data (index + leaf) record keys;
+ length of all index record keys if
+ btha_variableIndexKeysMask attribute flag is not set */
+ UINT32BE totalNodes; /* total number of nodes in tree */
+ UINT32BE freeNodes; /* number of unused (free) nodes in tree */
+
+ UINT16BE reserved1; /* unused */
+ UINT32BE clumpSize; /* used in some HFS implementations? (reserved in
+ early HFS implementations, and in HFS Plus) */
+ UINT8 btreeType; /* reserved - set to 0 */
+ UINT8 reserved2; /* reserved */
+ UINT32BE attributes; /* persistent attributes about the tree */
+ UINT32BE reserved3[16]; /* reserved */
+} BTHeaderRecord;
+
+static imgtoolerr_t BT_check(mac_BTref *BTref, int is_extent);
+
+/*
+ BT_open
+
+ Open a file as a B-tree. The file must be already open as a macintosh
+ file.
+
+ BTref (I/O): B-tree file handle to open (BTref->fileref must have been
+ open previously)
+ key_compare_func (I): function that compares two keys
+ is_extent (I): TRUE if we are opening the extent B-tree (we want to do
+ extra checks in this case because the extent B-Tree may include extent
+ records for the extent B-tree itself, and if an extent record for the
+ extent B-tree is located in an extent that has not been defined by
+ previous extent records, then we can never retreive this extent record)
+
+ Return imgtool error code
+*/
+static imgtoolerr_t BT_open(mac_BTref *BTref, int (*key_compare_func)(const void *key1, const void *key2), int is_extent)
+{
+ imgtoolerr_t err;
+ BTNodeHeader node_header;
+ BTHeaderRecord header_rec;
+
+ /* seek to node 0 */
+ err = mac_file_seek(&BTref->fileref, 0);
+ if (err)
+ return err;
+
+ /* read node header */
+ err = mac_file_read(&BTref->fileref, sizeof(node_header), &node_header);
+ if (err)
+ return err;
+
+ if ((node_header.kind != btnk_headerNode) || (get_UINT16BE(node_header.numRecords) < 3)
+ || (node_header.height != 0))
+ return IMGTOOLERR_CORRUPTIMAGE; /* right??? */
+
+ /* CHEESY HACK: we assume that the header record immediately follows the
+ node header. This is because we need to know the node length to know where
+ the record pointers are located, but we need to read the header record to
+ know the node length. */
+ err = mac_file_read(&BTref->fileref, sizeof(header_rec), &header_rec);
+ if (err)
+ return err;
+
+ BTref->nodeSize = get_UINT16BE(header_rec.nodeSize);
+ BTref->rootNode = get_UINT32BE(header_rec.rootNode);
+ BTref->firstLeafNode = get_UINT32BE(header_rec.firstLeafNode);
+ BTref->attributes = get_UINT32BE(header_rec.attributes);
+ BTref->treeDepth = get_UINT16BE(header_rec.treeDepth);
+ BTref->maxKeyLength = get_UINT16BE(header_rec.maxKeyLength);
+
+ BTref->key_compare_func = key_compare_func;
+
+ BTref->node_buf = malloc(BTref->nodeSize);
+ if (!BTref->node_buf)
+ return IMGTOOLERR_OUTOFMEMORY;
+
+ if (BTREE_CHECKS)
+ {
+ /* optional: check integrity of B-tree */
+ err = BT_check(BTref, is_extent);
+ if (err)
+ return err;
+ }
+
+ return IMGTOOLERR_SUCCESS;
+}
+
+/*
+ BT_close
+
+ Close a B-tree
+
+ BTref (I/O): open B-tree file handle
+*/
+static void BT_close(mac_BTref *BTref)
+{
+ free(BTref->node_buf);
+}
+
+/*
+ BT_read_node
+
+ Read a node from a B-tree
+
+ BTref (I/O): open B-tree file handle
+ node_ID (I): index of the node to read
+ expected_kind (I): kind of the node to read
+ expected_depth (I): depth of the node to read
+ dest (O): destination buffer
+
+ Return imgtool error code
+*/
+static imgtoolerr_t BT_read_node(mac_BTref *BTref, UINT32 node_ID, int expected_kind, int expected_depth, void *dest)
+{
+ imgtoolerr_t err;
+
+ /* seek to node */
+ err = mac_file_seek(&BTref->fileref, node_ID*BTref->nodeSize);
+ if (err)
+ return err;
+
+ /* read it */
+ err = mac_file_read(&BTref->fileref, BTref->nodeSize, dest);
+ if (err)
+ return err;
+
+ /* check node kind and depth */
+ if ((((BTNodeHeader *) dest)->kind != expected_kind)
+ || (((BTNodeHeader *) dest)->height != expected_depth))
+ return IMGTOOLERR_CORRUPTIMAGE;
+
+ return IMGTOOLERR_SUCCESS;
+}
+
+/*
+ BT_node_get_record
+
+ Extract a raw record from a B-tree node
+
+ BTref (I/O): open B-tree file handle
+ node_buf (I): buffer with the node the record should be extracted from
+ recnum (I): index of record to read
+ rec_ptr (O): set to point to start of record (key + data)
+ rec_len (O): set to total length of record (key + data)
+
+ Return imgtool error code
+*/
+static imgtoolerr_t BT_node_get_record(mac_BTref *BTref, void *node_buf, unsigned recnum, void **rec_ptr, int *rec_len)
+{
+ UINT16 node_numRecords = get_UINT16BE(((BTNodeHeader *) node_buf)->numRecords);
+ UINT16 offset;
+ UINT16 next_offset;
+
+ if (recnum >= node_numRecords)
+ return IMGTOOLERR_UNEXPECTED;
+
+ offset = get_UINT16BE(((UINT16BE *)((UINT8 *) node_buf + BTref->nodeSize))[-recnum-1]);
+ next_offset = get_UINT16BE(((UINT16BE *)((UINT8 *) node_buf + BTref->nodeSize))[-recnum-2]);
+
+ if ((offset < sizeof(BTNodeHeader)) || (offset > BTref->nodeSize-2*node_numRecords)
+ || (next_offset < sizeof(BTNodeHeader)) || (next_offset > BTref->nodeSize-2*node_numRecords)
+ || (offset & 1) || (next_offset & 1)
+ || (offset > next_offset))
+ return IMGTOOLERR_CORRUPTIMAGE;
+
+ *rec_ptr = (UINT8 *)node_buf + offset;
+ *rec_len = next_offset - offset;
+
+ return IMGTOOLERR_SUCCESS;
+}
+
+/*
+ BT_node_get_keyed_record
+
+ Extract a keyed record from a B-tree node. Equivalent to
+ BT_node_get_record, only we do extra checks.
+
+ BTref (I/O): open B-tree file handle
+ node_buf (I): buffer with the node the record should be extracted from
+ node_is_index (I): TRUE if node is index node
+ recnum (I): index of record to read
+ rec_ptr (O): set to point to start of record (key + data)
+ rec_len (O): set to total length of record (key + data)
+
+ Return imgtool error code
+*/
+static imgtoolerr_t BT_node_get_keyed_record(mac_BTref *BTref, void *node_buf, int node_is_index, unsigned recnum, void **rec_ptr, int *rec_len)
+{
+ imgtoolerr_t err;
+ void *lrec_ptr;
+ int lrec_len;
+ int key_len;
+
+ /* extract record */
+ err = BT_node_get_record(BTref, node_buf, recnum, &lrec_ptr, &lrec_len);
+ if (err)
+ return err;
+
+ /* read key len */
+ key_len = (BTref->attributes & btha_bigKeysMask)
+ ? get_UINT16BE(* (UINT16BE *)lrec_ptr)
+ : (* (UINT8 *)lrec_ptr);
+
+ /* check that key fits in record */
+ if ((key_len + ((BTref->attributes & btha_bigKeysMask) ? 2 : 1)) > lrec_len)
+ /* hurk! */
+ return IMGTOOLERR_CORRUPTIMAGE;
+
+ if (key_len > BTref->maxKeyLength)
+ return IMGTOOLERR_CORRUPTIMAGE;
+
+ if (node_is_index && (! (BTref->attributes & btha_variableIndexKeysMask)) && (key_len != BTref->maxKeyLength))
+ return IMGTOOLERR_CORRUPTIMAGE;
+
+ if (rec_ptr)
+ *rec_ptr = lrec_ptr;
+ if (rec_len)
+ *rec_len = lrec_len;
+
+ return IMGTOOLERR_SUCCESS;
+}
+
+/*
+ BT_get_keyed_record_data
+
+ extract data from a keyed record
+
+ BTref (I/O): open B-tree file handle
+ rec_ptr (I): point to start of record (key + data)
+ rec_len (I): total length of record (key + data)
+ data_ptr (O): set to point to record data
+ data_len (O): set to length of record data
+
+ Return imgtool error code
+*/
+static imgtoolerr_t BT_get_keyed_record_data(mac_BTref *BTref, void *rec_ptr, int rec_len, void **data_ptr, int *data_len)
+{
+ int lkey_len;
+ int data_offset;
+
+ /* read key len */
+ lkey_len = (BTref->attributes & btha_bigKeysMask)
+ ? get_UINT16BE(* (UINT16BE *)rec_ptr)
+ : (* (UINT8 *)rec_ptr);
+
+ /* compute offset to data record */
+ data_offset = lkey_len + ((BTref->attributes & btha_bigKeysMask) ? 2 : 1);
+ if (data_offset > rec_len)
+ /* hurk! */
+ return IMGTOOLERR_CORRUPTIMAGE;
+ /* fix alignment */
+ if (data_offset & 1)
+ data_offset++;
+
+ if (data_ptr)
+ *data_ptr = (UINT8 *)rec_ptr + data_offset;
+ if (data_len)
+ *data_len = (rec_len > data_offset) ? rec_len-data_offset : 0;
+
+ return IMGTOOLERR_SUCCESS;
+}
+
+/*
+ BT_check
+
+ Check integrity of a complete B-tree
+
+ BTref (I/O): open B-tree file handle
+ is_extent (I): TRUE if we are opening the extent B-tree (we want to do
+ extra checks in this case because the extent B-Tree may include extent
+ records for the extent B-tree itself, and if an extent record for the
+ extent B-tree is located in an extent that has not been defined by
+ previous extent records, then we can never retreive this extent record)
+
+ Return imgtool error code
+*/
+typedef struct data_nodes_t
+{
+ void *buf;
+ UINT32 node_num;
+ UINT32 cur_rec;
+ UINT32 num_recs;
+} data_nodes_t;
+static imgtoolerr_t BT_check(mac_BTref *BTref, int is_extent)
+{
+ UINT16 node_numRecords;
+ BTHeaderRecord *header_rec;
+ UINT8 *bitmap;
+
+ data_nodes_t *data_nodes;
+ int i, j;
+ UINT32 cur_node, prev_node;
+ void *rec1, *rec2;
+ int rec1_len, rec2_len;
+ void *rec1_data;
+ int rec1_data_len;
+ UINT32 totalNodes, lastLeafNode;
+ UINT32 freeNodes;
+ int compare_result;
+ UINT32 map_count, map_len;
+ UINT32 run_len;
+ UINT32 run_bit_len;
+ UINT32 actualFreeNodes;
+ imgtoolerr_t err;
+ UINT32 maxExtentAB = 0, maxExtentNode = 0, extentEOL = 0; /* if is_extent is TRUE */
+
+ if (is_extent)
+ {
+ switch (BTref->fileref.l2_img->format)
+ {
+ case L2I_MFS:
+ /* MFS does not feature any extents B-tree! */
+ return IMGTOOLERR_UNEXPECTED;
+
+ case L2I_HFS:
+ maxExtentAB = 0;
+ for (j=0; j<3; j++)
+ maxExtentAB += get_UINT16BE(BTref->fileref.u.hfs.extents[j].numABlks);
+ maxExtentNode = (UINT64)maxExtentAB * 512 * BTref->fileref.l2_img->blocksperAB
+ / BTref->nodeSize;
+ extentEOL = FALSE;
+ break;
+ }
+ }
+
+ /* read header node */
+ if ((! is_extent) || (0 < maxExtentNode))
+ err = BT_read_node(BTref, 0, btnk_headerNode, 0, BTref->node_buf);
+ else
+ err = IMGTOOLERR_CORRUPTIMAGE;
+ if (err)
+ return err;
+
+ /* check we have enough records */
+ node_numRecords = get_UINT16BE(((BTNodeHeader *) BTref->node_buf)->numRecords);
+ if (node_numRecords < 3)
+ return IMGTOOLERR_CORRUPTIMAGE;
+
+ /* get header record */
+ err = BT_node_get_record(BTref, BTref->node_buf, 0, &rec1, &rec1_len);
+ if (err)
+ return err;
+ header_rec = (BTHeaderRecord *)rec1;
+
+ /* check length of header record */
+ if (rec1_len < sizeof(BTHeaderRecord))
+ return IMGTOOLERR_CORRUPTIMAGE;
+
+ totalNodes = get_UINT32BE(header_rec->totalNodes);
+ if (totalNodes == 0)
+ /* we need at least one header node */
+ return IMGTOOLERR_CORRUPTIMAGE;
+ lastLeafNode = get_UINT32BE(header_rec->lastLeafNode);
+ freeNodes = get_UINT32BE(header_rec->freeNodes);
+
+ /* check file length */
+ if ((BTref->nodeSize * totalNodes) > BTref->fileref.pLen)
+ return IMGTOOLERR_CORRUPTIMAGE;
+
+ /* initialize for the function postlog ("bail:" tag) */
+ err = IMGTOOLERR_SUCCESS;
+ bitmap = NULL;
+ data_nodes = NULL;
+
+ /* alloc buffer for reconstructed bitmap */
+ map_len = (totalNodes + 7) / 8;
+ bitmap = (UINT8*)malloc(map_len);
+ if (! bitmap)
+ return IMGTOOLERR_OUTOFMEMORY;
+ memset(bitmap, 0, map_len);
+
+ /* check B-tree data nodes (i.e. index and leaf nodes) */
+ if (BTref->treeDepth == 0)
+ {
+ /* B-tree is empty */
+ if (BTref->rootNode || BTref->firstLeafNode || lastLeafNode)
+ {
+ err = IMGTOOLERR_OUTOFMEMORY;
+ goto bail;
+ }
+ }
+ else
+ {
+ /* alloc array of buffers for catalog data nodes */
+ data_nodes = (data_nodes_t *)malloc(sizeof(data_nodes_t) * BTref->treeDepth);
+ if (! data_nodes)
+ {
+ err = IMGTOOLERR_OUTOFMEMORY;
+ goto bail;
+ }
+ for (i=0; i<BTref->treeDepth; i++)
+ data_nodes[i].buf = NULL; /* required for function postlog to work should next loop fail */
+ for (i=0; i<BTref->treeDepth; i++)
+ {
+ data_nodes[i].buf = malloc(BTref->nodeSize);
+ if (!data_nodes[i].buf)
+ {
+ err = IMGTOOLERR_OUTOFMEMORY;
+ goto bail;
+ }
+ }
+
+ /* read first data nodes */
+ cur_node = BTref->rootNode;
+ for (i=BTref->treeDepth-1; i>=0; i--)
+ {
+ /* check node index */
+ if (cur_node >= totalNodes)
+ {
+ err = IMGTOOLERR_CORRUPTIMAGE;
+ goto bail;
+ }
+ /* check that node has not been used for another purpose */
+ /* this check is unecessary because the current consistency checks
+ that forward and back linking match and that node height is correct
+ are enough to detect such errors */
+#if 0
+ if (bitmap[cur_node >> 3] & (0x80 >> (cur_node & 7)))
+ {
+ err = IMGTOOLERR_CORRUPTIMAGE;
+ goto bail;
+ }
+#endif
+ /* add node in bitmap */
+ bitmap[cur_node >> 3] |= (0x80 >> (cur_node & 7));
+ /* read node */
+ if ((! is_extent) || (cur_node < maxExtentNode))
+ err = BT_read_node(BTref, cur_node, i ? btnk_indexNode : btnk_leafNode, i+1, data_nodes[i].buf);
+ else
+ err = IMGTOOLERR_CORRUPTIMAGE;
+ if (err)
+ goto bail;
+ /* check that it is the first node at this level */
+ if (get_UINT32BE(((BTNodeHeader *) data_nodes[i].buf)->bLink))
+ {
+ err = IMGTOOLERR_CORRUPTIMAGE;
+ goto bail;
+ }
+ /* fill other fields */
+ data_nodes[i].node_num = cur_node;
+ data_nodes[i].cur_rec = 0;
+ data_nodes[i].num_recs = get_UINT16BE(((BTNodeHeader *) data_nodes[i].buf)->numRecords);
+ /* check that there is at least one record */
+ if (data_nodes[i].num_recs == 0)
+ {
+ err = IMGTOOLERR_CORRUPTIMAGE;
+ goto bail;
+ }
+
+ /* iterate to next level if applicable */
+ if (i != 0)
+ {
+ /* extract first record */
+ err = BT_node_get_keyed_record(BTref, data_nodes[i].buf, TRUE, 0, &rec1, &rec1_len);
+ if (err)
+ goto bail;
+
+ /* extract record data ptr */
+ err = BT_get_keyed_record_data(BTref, rec1, rec1_len, &rec1_data, &rec1_data_len);
+ if (err)
+ goto bail;
+ if (rec1_data_len < sizeof(UINT32BE))
+ {
+ err = IMGTOOLERR_CORRUPTIMAGE;
+ goto bail;
+ }
+
+ /* iterate to next level */
+ cur_node = get_UINT32BE(* (UINT32BE *)rec1_data);
+ }
+ }
+
+ /* check that a) the root node has no successor, and b) that we have really
+ read the first leaf node */
+ if (get_UINT32BE(((BTNodeHeader *) data_nodes[BTref->treeDepth-1].buf)->fLink)
+ || (cur_node != BTref->firstLeafNode))
+ {
+ err = IMGTOOLERR_CORRUPTIMAGE;
+ goto bail;
+ }
+
+ /* check that keys are ordered correctly */
+ while (1)
+ {
+ /* iterate through parent nodes */
+ i = 0;
+ while ((i<BTref->treeDepth) && ((data_nodes[i].cur_rec == 0) || (data_nodes[i].cur_rec == data_nodes[i].num_recs)))
+ {
+ /* read next node if necessary */
+ if (data_nodes[i].cur_rec == data_nodes[i].num_recs)
+ {
+ /* get link to next node */
+ cur_node = get_UINT32BE(((BTNodeHeader *) data_nodes[i].buf)->fLink);
+ if (cur_node == 0)
+ {
+ if (i == 0)
+ /* normal End of List */
+ goto end_of_list;
+ else
+ {
+ /* error */
+ err = IMGTOOLERR_CORRUPTIMAGE;
+ goto bail;
+ }
+ }
+ /* add node in bitmap */
+ bitmap[cur_node >> 3] |= (0x80 >> (cur_node & 7));
+ /* read node */
+ if ((! is_extent) || (cur_node < maxExtentNode))
+ err = BT_read_node(BTref, cur_node, i ? btnk_indexNode : btnk_leafNode, i+1, data_nodes[i].buf);
+ else
+ err = IMGTOOLERR_CORRUPTIMAGE;
+ if (err)
+ goto bail;
+ /* check that backward linking match forward linking */
+ if (get_UINT32BE(((BTNodeHeader *) data_nodes[i].buf)->bLink) != data_nodes[i].node_num)
+ {
+ err = IMGTOOLERR_CORRUPTIMAGE;
+ goto bail;
+ }
+ /* fill other fields */
+ data_nodes[i].node_num = cur_node;
+ data_nodes[i].cur_rec = 0;
+ data_nodes[i].num_recs = get_UINT16BE(((BTNodeHeader *) data_nodes[i].buf)->numRecords);
+ /* check that there is at least one record */
+ if (data_nodes[i].num_recs == 0)
+ {
+ err = IMGTOOLERR_CORRUPTIMAGE;
+ goto bail;
+ }
+ /* next test is not necessary because we have checked that
+ the root node has no successor */
+#if 0
+ if (i < BTref->treeDepth-1)
+ {
+#endif
+ data_nodes[i+1].cur_rec++;
+#if 0
+ }
+ else
+ {
+ err = IMGTOOLERR_CORRUPTIMAGE;
+ goto bail;
+ }
+#endif
+ }
+ i++;
+ }
+
+ if (is_extent && !extentEOL)
+ {
+ /* extract current leaf record and update maxExtentAB and
+ maxExtentNode */
+ hfs_extentKey *extentKey;
+ hfs_extent *extentData;
+
+ /* extract current leaf record */
+ err = BT_node_get_keyed_record(BTref, data_nodes[0].buf, FALSE, data_nodes[0].cur_rec, &rec1, &rec1_len);
+ if (err)
+ goto bail;
+
+ extentKey = (hfs_extentKey*)rec1;
+ if ((extentKey->keyLength < 7) || (extentKey->forkType != 0) || (get_UINT32BE(extentKey->fileID) != 3)
+ || (get_UINT16BE(extentKey->startBlock) != maxExtentAB))
+ /* the key is corrupt or does not concern the extent
+ B-tree: set the extentEOL flag so that we stop looking for
+ further extent records for the extent B-tree */
+ extentEOL = TRUE;
+ else
+ { /* this key concerns the extent B-tree: update maxExtentAB
+ and maxExtentNode */
+ /* extract record data ptr */
+ err = BT_get_keyed_record_data(BTref, rec1, rec1_len, &rec1_data, &rec1_data_len);
+ if (err)
+ goto bail;
+ if (rec1_data_len < sizeof(hfs_extent)*3)
+ /* the record is corrupt: set the extentEOL flag so
+ that we stop looking for further extent records for the
+ extent B-tree */
+ extentEOL = TRUE;
+ else
+ {
+ extentData = (hfs_extent*)rec1_data;
+
+ for (j=0; j<3; j++)
+ maxExtentAB += get_UINT16BE(extentData[j].numABlks);
+ maxExtentNode = (UINT64)maxExtentAB * 512 * BTref->fileref.l2_img->blocksperAB
+ / BTref->nodeSize;
+ }
+ }
+ if (extentEOL)
+ {
+ /* check that the extent B-Tree has been defined entirely */
+ if (maxExtentNode < totalNodes)
+ { /* no good */
+ err = IMGTOOLERR_CORRUPTIMAGE;
+ goto bail;
+ }
+ }
+ }
+
+ if (i<BTref->treeDepth)
+ {
+ /* extract current record */
+ err = BT_node_get_keyed_record(BTref, data_nodes[i].buf, i > 0, data_nodes[i].cur_rec, &rec1, &rec1_len);
+ if (err)
+ goto bail;
+
+ /* extract previous record */
+ err = BT_node_get_keyed_record(BTref, data_nodes[i].buf, i > 0, data_nodes[i].cur_rec-1, &rec2, &rec2_len);
+ if (err)
+ goto bail;
+
+ /* check that it is sorted correctly */
+ compare_result = (*BTref->key_compare_func)(rec1, rec2);
+ if (compare_result <= 0)
+ {
+ err = IMGTOOLERR_CORRUPTIMAGE;
+ goto bail;
+ }
+
+ i--;
+ }
+ else
+ {
+ i--;
+ if (i>0)
+ { /* extract first record of root if it is an index node */
+ err = BT_node_get_keyed_record(BTref, data_nodes[i].buf, TRUE, data_nodes[i].cur_rec, &rec1, &rec1_len);
+ if (err)
+ goto bail;
+ }
+ i--;
+ }
+
+ while (i>=0)
+ {
+ /* extract first record of current level */
+ err = BT_node_get_keyed_record(BTref, data_nodes[i].buf, i > 0, data_nodes[i].cur_rec, &rec2, &rec2_len);
+ if (err)
+ goto bail;
+
+ /* compare key with key of current record of upper level */
+ compare_result = (*BTref->key_compare_func)(rec1, rec2);
+ if (compare_result != 0)
+ {
+ err = IMGTOOLERR_CORRUPTIMAGE;
+ goto bail;
+ }
+
+ /* extract record data ptr */
+ err = BT_get_keyed_record_data(BTref, rec1, rec1_len, &rec1_data, &rec1_data_len);
+ if (err)
+ goto bail;
+ if (rec1_data_len < sizeof(UINT32BE))
+ {
+ err = IMGTOOLERR_CORRUPTIMAGE;
+ goto bail;
+ }
+ cur_node = get_UINT32BE(* (UINT32BE *)rec1_data);
+
+ /* compare node index with data of current record of upper
+ level */
+ if (cur_node != data_nodes[i].node_num)
+ {
+ err = IMGTOOLERR_CORRUPTIMAGE;
+ goto bail;
+ }
+
+ /* iterate to next level */
+ rec1 = rec2;
+ rec1_len = rec2_len;
+ i--;
+ }
+
+ /* next leaf record */
+ data_nodes[0].cur_rec++;
+ }
+
+end_of_list:
+ /* check that we are at the end of list for each index level */
+ for (i=1; i<BTref->treeDepth; i++)
+ {
+ if ((data_nodes[i].cur_rec != (data_nodes[i].num_recs-1))
+ || get_UINT32BE(((BTNodeHeader *) data_nodes[i].buf)->fLink))
+ {
+ err = IMGTOOLERR_CORRUPTIMAGE;
+ goto bail;
+ }
+ }
+ /* check that the last leaf node is what it is expected to be */
+ if (data_nodes[0].node_num != lastLeafNode)
+ {
+ err = IMGTOOLERR_CORRUPTIMAGE;
+ goto bail;
+ }
+ }
+
+ /* check map node chain */
+ cur_node = 0; /* node 0 is the header node... */
+ bitmap[0] |= 0x80;
+ /* check back linking */
+ if (get_UINT32BE(((BTNodeHeader *) BTref->node_buf)->bLink))
+ {
+ err = IMGTOOLERR_CORRUPTIMAGE;
+ goto bail;
+ }
+ /* get pointer to next node */
+ cur_node = get_UINT32BE(((BTNodeHeader *) BTref->node_buf)->fLink);
+ while (cur_node != 0)
+ {
+ /* save node address */
+ prev_node = cur_node;
+ /* check that node has not been used for another purpose */
+ /* this check is unecessary because the current consistency checks that
+ forward and back linking match and that node height is correct are
+ enough to detect such errors */
+#if 0
+ if (bitmap[cur_node >> 3] & (0x80 >> (cur_node & 7)))
+ {
+ err = IMGTOOLERR_CORRUPTIMAGE;
+ goto bail;
+ }
+#endif
+ /* add node in bitmap */
+ bitmap[cur_node >> 3] |= (0x80 >> (cur_node & 7));
+ /* read map node */
+ if ((! is_extent) || (cur_node < maxExtentNode))
+ err = BT_read_node(BTref, cur_node, btnk_mapNode, 0, BTref->node_buf);
+ else
+ err = IMGTOOLERR_CORRUPTIMAGE;
+ if (err)
+ goto bail;
+ /* check back linking */
+ if (get_UINT32BE(((BTNodeHeader *) BTref->node_buf)->bLink) != prev_node)
+ {
+ err = IMGTOOLERR_CORRUPTIMAGE;
+ goto bail;
+ }
+ /* get pointer to next node */
+ cur_node = get_UINT32BE(((BTNodeHeader *) BTref->node_buf)->fLink);
+ }
+
+ /* re-read header node */
+ err = BT_read_node(BTref, 0, btnk_headerNode, 0, BTref->node_buf);
+ if (err)
+ goto bail;
+
+ /* get header bitmap record */
+ err = BT_node_get_record(BTref, BTref->node_buf, 2, &rec1, &rec1_len);
+ if (err)
+ goto bail;
+
+ /* check bitmap, iterating map nodes */
+ map_count = 0;
+ actualFreeNodes = 0;
+ while (map_count < map_len)
+ {
+ /* compute compare len */
+ run_len = rec1_len;
+ if (run_len > (map_len-map_count))
+ run_len = map_len-map_count;
+ /* check that all used nodes are marked as such in the B-tree bitmap */
+ for (i=0; i<run_len; i++)
+ if (bitmap[map_count+i] & ~((UINT8 *)rec1)[i])
+ {
+ err = IMGTOOLERR_CORRUPTIMAGE;
+ goto bail;
+ }
+ /* count free nodes */
+ run_bit_len = rec1_len*8;
+ if (run_bit_len > (totalNodes-map_count*8))
+ run_bit_len = totalNodes-map_count*8;
+ for (i=0; i<run_bit_len; i++)
+ if (! (((UINT8 *)rec1)[i>>3] & (0x80 >> (i & 7))))
+ actualFreeNodes++;
+ map_count += run_len;
+ /* read next map node if required */
+ if (map_count < map_len)
+ {
+ /* get pointer to next node */
+ cur_node = get_UINT32BE(((BTNodeHeader *) BTref->node_buf)->fLink);
+ if (cur_node == 0)
+ {
+ err = IMGTOOLERR_CORRUPTIMAGE;
+ goto bail;
+ }
+ /* read map node */
+ err = BT_read_node(BTref, cur_node, btnk_mapNode, 0, BTref->node_buf);
+ if (err)
+ goto bail;
+ /* get map record */
+ err = BT_node_get_record(BTref, BTref->node_buf, 0, &rec1, &rec1_len);
+ if (err)
+ goto bail;
+ header_rec = (BTHeaderRecord *)rec1;
+ }
+ }
+
+ /* check free node count */
+ if (freeNodes != actualFreeNodes)
+ return IMGTOOLERR_CORRUPTIMAGE;
+
+bail:
+ /* free buffers */
+ if (data_nodes)
+ {
+ for (i=0; i<BTref->treeDepth; i++)
+ if (data_nodes[i].buf)
+ free(data_nodes[i].buf);
+ free(data_nodes);
+ }
+ if (bitmap)
+ free(bitmap);
+
+ return err;
+}
+
+/*
+ BT_search_leaf_rec
+
+ Search for a given key in a B-Tree. If exact match found, returns
+ corresponding leaf record. Otherwise, may return the greatest record less
+ than the requested key (of course, this will fail if the key is lower than
+ all keys in the B-Tree).
+
+ BTref (I/O): open B-tree file handle
+ search_key (I): key to search the B-Tree for
+ node_ID (O): set to the node ID of the node the record is located in (may
+ be NULL)
+ record_ID (O): set to the index of the record in the node (may be NULL)
+ record_ptr (O): set to point to record in node buffer (may be NULL)
+ record_len (O): set to total record len (may be NULL)
+ search_exact_match (I): if TRUE, the function will search for a record
+ equal to search_key; if FALSE, the function will search for the
+ greatest record less than or equal to search_key
+ match_found (O): set to TRUE if an exact match for search_key has been
+ found (only makes sense if search_exact_match is FALSE) (may be NULL)
+
+ Return imgtool error code
+*/
+static imgtoolerr_t BT_search_leaf_rec(mac_BTref *BTref, const void *search_key,
+ UINT32 *node_ID, int *record_ID,
+ void **record_ptr, int *record_len,
+ int search_exact_match, int *match_found)
+{
+ imgtoolerr_t err;
+ int i;
+ UINT32 cur_node;
+ void *cur_rec;
+ int cur_rec_len;
+ void *last_rec;
+ int last_rec_len = 0;
+ void *rec_data;
+ int rec_data_len;
+ int depth;
+ UINT16 node_numRecords;
+ int compare_result = 0;
+
+ /* start with root node */
+ if ((BTref->rootNode == 0) || (BTref->treeDepth == 0))
+ /* tree is empty */
+ return ((BTref->rootNode == 0) == (BTref->treeDepth == 0))
+ ? IMGTOOLERR_FILENOTFOUND
+ : IMGTOOLERR_CORRUPTIMAGE;
+
+ cur_node = BTref->rootNode;
+ depth = BTref->treeDepth;
+
+ while (1)
+ {
+ /* read current node */
+ err = BT_read_node(BTref, cur_node, (depth > 1) ? btnk_indexNode : btnk_leafNode, depth, BTref->node_buf);
+ if (err)
+ return err;
+
+ /* search for key */
+ node_numRecords = get_UINT16BE(((BTNodeHeader *) BTref->node_buf)->numRecords);
+ last_rec = cur_rec = NULL;
+ for (i=0; i<node_numRecords; i++)
+ {
+ err = BT_node_get_keyed_record(BTref, BTref->node_buf, depth > 1, i, &cur_rec, &cur_rec_len);
+ if (err)
+ return err;
+
+ compare_result = (*BTref->key_compare_func)(cur_rec, search_key);
+ if (compare_result > 0)
+ break;
+ last_rec = cur_rec;
+ last_rec_len = cur_rec_len;
+ if (compare_result == 0)
+ break;
+ }
+
+ if (! last_rec)
+ { /* all keys are greater than the search key: the search key is
+ nowhere in the tree */
+ if (search_exact_match)
+ return IMGTOOLERR_FILENOTFOUND;
+
+ if (match_found)
+ *match_found = FALSE;
+
+ if (node_ID)
+ *node_ID = 0;
+
+ if (record_ID)
+ *record_ID = -1;
+
+ if (record_ptr)
+ *record_ptr = NULL;
+
+ return IMGTOOLERR_SUCCESS;
+ }
+
+ if (((BTNodeHeader *) BTref->node_buf)->kind == btnk_leafNode)
+ /* leaf node -> end of search */
+ break;
+
+ /* extract record data ptr */
+ err = BT_get_keyed_record_data(BTref, last_rec, last_rec_len, &rec_data, &rec_data_len);
+ if (err)
+ return err;
+ if (rec_data_len < sizeof(UINT32BE))
+ return IMGTOOLERR_CORRUPTIMAGE;
+
+ /* iterate to next level */
+ cur_node = get_UINT32BE(* (UINT32BE *)rec_data);
+ depth--;
+ }
+
+ if (compare_result != 0)
+ /* key not found */
+ if (search_exact_match)
+ return IMGTOOLERR_FILENOTFOUND;
+
+ if (match_found)
+ *match_found = (compare_result == 0);
+
+ if (node_ID)
+ *node_ID = cur_node;
+
+ if (record_ID)
+ *record_ID = i;
+
+ if (record_ptr)
+ *record_ptr = last_rec;
+
+ if (record_len)
+ *record_len = last_rec_len;
+
+ return IMGTOOLERR_SUCCESS;
+}
+
+/*
+ BT_leaf_rec_enumerator_open
+
+ Open enumerator for leaf records of a B-Tree
+
+ BTref (I/O): open B-tree file handle
+ enumerator (O): B-Tree enumerator to open
+
+ Return imgtool error code
+*/
+static imgtoolerr_t BT_leaf_rec_enumerator_open(mac_BTref *BTref, BT_leaf_rec_enumerator *enumerator)
+{
+ enumerator->BTref = BTref;
+ enumerator->cur_node = BTref->firstLeafNode;
+ enumerator->cur_rec = 0;
+
+ return IMGTOOLERR_SUCCESS;
+}
+
+/*
+ BT_leaf_rec_enumerator_read
+
+ Read next leaf record of a B-Tree
+
+ enumerator (I/O): open B-Tree enumerator
+
+ Return imgtool error code
+*/
+static imgtoolerr_t BT_leaf_rec_enumerator_read(BT_leaf_rec_enumerator *enumerator, void **record_ptr, int *rec_len)
+{
+ UINT16 node_numRecords;
+ imgtoolerr_t err;
+
+
+ *record_ptr = NULL;
+
+ /* check EOList condition */
+ if (enumerator->cur_node == 0)
+ return IMGTOOLERR_SUCCESS;
+
+ /* read current node */
+ err = BT_read_node(enumerator->BTref, enumerator->cur_node, btnk_leafNode, 1, enumerator->BTref->node_buf);
+ if (err)
+ return err;
+ node_numRecords = get_UINT16BE(((BTNodeHeader *) enumerator->BTref->node_buf)->numRecords);
+
+ /* skip nodes until we find a record */
+ while ((enumerator->cur_rec >= node_numRecords) && (enumerator->cur_node != 0))
+ {
+ enumerator->cur_node = get_UINT32BE(((BTNodeHeader *) enumerator->BTref->node_buf)->fLink);
+ enumerator->cur_rec = 0;
+
+ /* read node */
+ err = BT_read_node(enumerator->BTref, enumerator->cur_node, btnk_leafNode, 1, enumerator->BTref->node_buf);
+ if (err)
+ return err;
+ node_numRecords = get_UINT16BE(((BTNodeHeader *) enumerator->BTref->node_buf)->numRecords);
+ }
+
+ /* check EOList condition */
+ if (enumerator->cur_node == 0)
+ return IMGTOOLERR_SUCCESS;
+
+ /* get current record */
+ err = BT_node_get_keyed_record(enumerator->BTref, enumerator->BTref->node_buf, FALSE, enumerator->cur_rec, record_ptr, rec_len);
+ if (err)
+ return err;
+
+ /* iterate to next record */
+ enumerator->cur_rec++;
+ if (enumerator->cur_rec >= node_numRecords)
+ { /* iterate to next node if last record (not required, but will improve
+ performance on next iteration) */
+ enumerator->cur_node = get_UINT32BE(((BTNodeHeader *) enumerator->BTref->node_buf)->fLink);
+ enumerator->cur_rec = 0;
+ }
+ return IMGTOOLERR_SUCCESS;
+}
+
+/*
+ B-Tree extend EOF algorithm:
+ * see if the bitmap will need to be extended
+ * extend EOF by min 1 (if bitmap is large engough) or 2 (if bitmap needs
+ to be extended) and max ClumpSiz (see extClpSiz and ctClpSiz in MDB)
+ ***If we are extending the extent B-Tree, we need to defer the possible
+ creation of an additional extent record, or we might enter an endless
+ recursion loop***
+
+ Empty node alloc algorithm:
+
+ * see if there is any free node in B-tree bitmap
+ * optionally, try to compact the B-tree if file is full
+ * if file is still full, extend EOF and try again
+ * mark new block as used and return its index
+
+
+ Empty node delete algorithm:
+
+ * remove node from link list
+ * mark node as free in the B-tree bitmap
+ * optionally, if more than N% of the B-tree is free, compact the B-tree and
+ free some disk space
+ * Count nodes on this level; if there is only one left, delete parent index
+ node and mark the relaining node as root; if it was the last leaf node,
+ update header node with an empty B-tree; in either case, decrement tree
+ depth
+
+
+ Record shifting algorithm:
+
+ For a given node and its first non-empty successor node:
+
+ * compute how much free room there is in the node
+ * see if the first record of the first non-empty successor can fit
+ * if so, move it (i.e. delete the first record of the later node, and add a
+ copy of it to the end of the former)
+
+
+ Node merging algorithm
+
+ * Consider node and its predecessor. If there is room, shift all records
+ from later to former, then delete empty later node, and delete later
+ record from parent index node.
+
+
+ Node splitting algorithm (non-first)
+
+ * Consider node and its predecessor. Create new middle node and split
+ records in 3 even sets. Update record for last node and insert record
+ for middle node in parent index node.
+
+
+ Node splitting algorithm (first node)
+
+ * Create new successor node, and split records in 2 1/3 and 2/3 sets.
+ Insert record for later node in parent index node.
+
+
+ Record delete algorithm:
+
+ * remove record from node
+ * if record was first in node, test if node is now empty
+ * if node is not empty, substitute key of deleted record with key of
+ new head record in index tree
+ * if node is empty, delete key of deleted record in index tree, then
+ delete empty node
+ * optionally, look the predecessor node. Merge the two nodes if possible.
+
+
+ Record insert algorithm:
+
+ * if there room, just insert new record in node; if new record is in first
+ position, update record in parent index node
+ * else consider predecessor: see if we can make enough room by shifting
+ records. If so, do shift records, insert new record, update record in
+ parent index node
+ * else split the nodes and insert record
+*/
+/*
+ Possible additions:
+
+ Node compaction algorithm:
+
+ This algorithm can be executed with a specific start point and max number
+ of nodes, or with all nodes on a level.
+
+ * see how many nodes we can save by shifting records left
+ * if we will save at least one node, do shift as many records as possible
+ (try to leave free space split homogeneously???)
+*/
+
+/*static void*/
+
+#if 0
+#pragma mark -
+#pragma mark RESOURCE IMPLEMENTATION
+#endif
+
+/*
+ Resource manager
+
+ The resource manager stores arbitrary chunks of data (resource) identified
+ by a type/id pair. The resource type is a 4-char code, which generally
+ implies the format of the data (e.g. 'PICT' is for a quickdraw picture,
+ 'STR ' for a macintosh string, 'CODE' for 68k machine code, etc). The
+ resource id is a signed 16-bit number that uniquely identifies each
+ resource of a given type. Note that, with most resource types, resources
+ with id < 128 are system resources that are available to all applications,
+ whereas resources with id >= 128 are application resources visible only to
+ the application that defines them.
+
+ Each resource can optionally have a resource name, which is a macintosh
+ string of 255 chars at most.
+
+ Limits:
+ 16MBytes of data
+ 64kbytes of type+reference lists
+ 64kbytes of resource names
+
+ The Macintosh toolbox can open several resource files simulteanously to
+ overcome these restrictions.
+
+ Resources are used virtually everywhere in the Macintosh Toolbox, so it is
+ no surprise that file comments and MFS folders are stored in resource files.
+*/
+
+/*
+ Resource header
+*/
+typedef struct rsrc_header
+{
+ UINT32BE data_offs; /* Offset from beginning of resource fork to resource data */
+ UINT32BE map_offs; /* Offset from beginning of resource fork to resource map */
+ UINT32BE data_len; /* Length of resource data */
+ UINT32BE map_len; /* Length of resource map */
+} rsrc_header;
+
+/*
+ Resource data: each data entry is preceded by its len (UINT32BE)
+ Offset to specific data fields are gotten from the resource map
+*/
+
+/*
+ Resource map:
+*/
+typedef struct rsrc_map_header
+{
+ rsrc_header reserved0; /* Reserved for copy of resource header */
+ UINT32BE reserved1; /* Reserved for handle to next resource map */
+ UINT16BE reserved2; /* Reserved for file reference number */
+
+ UINT16BE attr; /* Resource fork attributes */
+ UINT16BE typelist_offs; /* Offset from beginning of map to resource type list */
+ UINT16BE namelist_offs; /* Offset from beginning of map to resource name list */
+ UINT16BE type_count; /* Number of types in the map minus 1 */
+ /* This is actually part of the type list, which matters for offsets */
+} rsrc_map_header;
+
+/*
+ Resource type list entry
+*/
+typedef struct rsrc_type_entry
+{
+ UINT32BE type; /* Resource type */
+ UINT16BE ref_count; /* Number of resources of this type in map minus 1 */
+ UINT16BE ref_offs; /* Offset from beginning of resource type list to reference list for this type */
+} rsrc_type_entry;
+
+/*
+ Resource reference list entry
+*/
+typedef struct rsrc_ref_entry
+{
+ UINT16BE id; /* Resource ID */
+ UINT16BE name_offs; /* Offset from beginning of resource name list to resource name */
+ /* (-1 if none) */
+ UINT8 attr; /* Resource attributes */
+ UINT24BE data_offs; /* Offset from beginning of resource data to data for this resource */
+ UINT32BE reserved; /* Reserved for handle to resource */
+} rsrc_ref_entry;
+
+/*
+ Resource name list entry: this is just a standard macintosh string
+*/
+
+typedef struct mac_resfileref
+{
+ mac_fileref fileref; /* open resource fork ref (you may open resources
+ files in data fork, too, if you ever need to,
+ but Classic MacOS never does such a thing
+ (MacOS X often does so, though)) */
+ UINT32 data_offs; /* Offset from beginning of resource file to resource data */
+ UINT32 map_offs; /* Offset from beginning of resource file to resource data */
+
+ UINT16 typelist_offs; /* Offset from beginning of map to resource type list */
+ UINT16 namelist_offs; /* Offset from beginning of map to resource name list */
+ UINT16 type_count; /* Number of types in the map minus 1 */
+ /* This is actually part of the type list, which matters for offsets */
+} mac_resfileref;
+
+#ifdef UNUSED_FUNCTION
+/*
+ resfile_open
+
+ Open a file as a resource file. The file must be already open as a
+ macintosh file.
+
+ resfileref (I/O): resource file handle to open (resfileref->fileref must
+ have been opened previously)
+
+ Return imgtool error code
+*/
+static imgtoolerr_t resfile_open(mac_resfileref *resfileref)
+{
+ imgtoolerr_t err;
+ rsrc_header header;
+ rsrc_map_header map_header;
+
+ /* seek to resource header */
+ err = mac_file_seek(&resfileref->fileref, 0);
+ if (err)
+ return err;
+
+ err = mac_file_read(&resfileref->fileref, sizeof(header), &header);
+ if (err)
+ return err;
+
+ resfileref->data_offs = get_UINT32BE(header.data_offs);
+ resfileref->map_offs = get_UINT32BE(header.map_offs);
+
+ /* seek to resource map header */
+ err = mac_file_seek(&resfileref->fileref, resfileref->map_offs);
+ if (err)
+ return err;
+
+ err = mac_file_read(&resfileref->fileref, sizeof(map_header), &map_header);
+ if (err)
+ return err;
+
+ resfileref->typelist_offs = get_UINT16BE(map_header.typelist_offs);
+ resfileref->namelist_offs = get_UINT16BE(map_header.namelist_offs);
+ resfileref->type_count = get_UINT16BE(map_header.type_count);
+
+ return IMGTOOLERR_SUCCESS;
+}
+
+/*
+ resfile_get_entry
+
+ Get the resource entry in the resource map associated with a given type/id
+ pair.
+
+ resfileref (I/O): open resource file handle
+ type (I): type of the resource
+ id (I): id of the resource
+ entry (O): resource entry that has been read
+
+ Return imgtool error code
+*/
+static imgtoolerr_t resfile_get_entry(mac_resfileref *resfileref, UINT32 type, UINT16 id, rsrc_ref_entry *entry)
+{
+ imgtoolerr_t err;
+ rsrc_type_entry type_entry;
+ UINT16 ref_count;
+ int i;
+
+ /* seek to resource type list in resource map */
+ err = mac_file_seek(&resfileref->fileref, resfileref->map_offs+resfileref->typelist_offs+2);
+ if (err)
+ return err;
+
+ if (resfileref->type_count == 0xffff)
+ /* type list is empty */
+ return IMGTOOLERR_FILENOTFOUND;
+
+ for (i=0; i<=resfileref->type_count; i++)
+ {
+ err = mac_file_read(&resfileref->fileref, sizeof(type_entry), &type_entry);
+ if (err)
+ return err;
+ if (type == get_UINT32BE(type_entry.type))
+ break;
+ }
+ if (i > resfileref->type_count)
+ /* type not found in list */
+ return IMGTOOLERR_FILENOTFOUND;
+
+ ref_count = get_UINT16BE(type_entry.ref_count);
+
+ /* seek to resource ref list for this type in resource map */
+ err = mac_file_seek(&resfileref->fileref, resfileref->map_offs+resfileref->typelist_offs+get_UINT16BE(type_entry.ref_offs));
+ if (err)
+ return err;
+
+ if (ref_count == 0xffff)
+ /* ref list is empty */
+ return IMGTOOLERR_FILENOTFOUND;
+
+ for (i=0; i<=ref_count; i++)
+ {
+ err = mac_file_read(&resfileref->fileref, sizeof(*entry), entry);
+ if (err)
+ return err;
+ if (id == get_UINT16BE(entry->id))
+ break;
+ }
+ if (i > ref_count)
+ /* id not found in list */
+ return IMGTOOLERR_FILENOTFOUND;
+
+ /* type+id have been found... */
+ return IMGTOOLERR_SUCCESS;
+}
+
+/*
+ resfile_get_resname
+
+ Get the name of a resource.
+
+ resfileref (I/O): open resource file handle
+ entry (I): resource entry in the resource map (returned by
+ resfile_get_entry)
+ string (O): resource name
+
+ Return imgtool error code
+*/
+static imgtoolerr_t resfile_get_resname(mac_resfileref *resfileref, const rsrc_ref_entry *entry, mac_str255 string)
+{
+ imgtoolerr_t err;
+ UINT16 name_offs;
+ UINT8 len;
+
+ name_offs = get_UINT16BE(entry->name_offs);
+
+ if (name_offs == 0xffff)
+ /* ref list is empty */
+ return IMGTOOLERR_UNEXPECTED;
+
+ /* seek to resource name in name list in resource map */
+ err = mac_file_seek(&resfileref->fileref, resfileref->map_offs+name_offs);
+ if (err)
+ return err;
+
+ /* get string length */
+ err = mac_file_read(&resfileref->fileref, 1, &len);
+ if (err)
+ return err;
+
+ string[0] = len;
+
+ /* get string data */
+ err = mac_file_read(&resfileref->fileref, len, string+1);
+ if (err)
+ return err;
+
+ return IMGTOOLERR_SUCCESS;
+}
+
+/*
+ resfile_get_reslen
+
+ Get the data length for a given resource.
+
+ resfileref (I/O): open resource file handle
+ entry (I): resource entry in the resource map (returned by
+ resfile_get_entry)
+ len (O): resource length
+
+ Return imgtool error code
+*/
+static imgtoolerr_t resfile_get_reslen(mac_resfileref *resfileref, const rsrc_ref_entry *entry, UINT32 *len)
+{
+ imgtoolerr_t err;
+ UINT32 data_offs;
+ UINT32BE llen;
+
+ data_offs = get_UINT24BE(entry->data_offs);
+
+ /* seek to resource data in resource data section */
+ err = mac_file_seek(&resfileref->fileref, resfileref->data_offs+data_offs);
+ if (err)
+ return err;
+
+ /* get data length */
+ err = mac_file_read(&resfileref->fileref, sizeof(llen), &llen);
+ if (err)
+ return err;
+
+ *len = get_UINT32BE(llen);
+
+ return IMGTOOLERR_SUCCESS;
+}
+
+/*
+ resfile_get_resdata
+
+ Get the data for a given resource.
+
+ resfileref (I/O): open resource file handle
+ entry (I): resource entry in the resource map (returned by
+ resfile_get_entry)
+ offset (I): offset the data should be read from, usually 0
+ len (I): length of the data to read, usually the value returned by
+ resfile_get_reslen
+ dest (O): resource data
+
+ Return imgtool error code
+*/
+static imgtoolerr_t resfile_get_resdata(mac_resfileref *resfileref, const rsrc_ref_entry *entry, UINT32 offset, UINT32 len, void *dest)
+{
+ imgtoolerr_t err;
+ UINT32 data_offs;
+ UINT32BE llen;
+
+ data_offs = get_UINT24BE(entry->data_offs);
+
+ /* seek to resource data in resource data section */
+ err = mac_file_seek(&resfileref->fileref, resfileref->data_offs+data_offs);
+ if (err)
+ return err;
+
+ /* get data length */
+ err = mac_file_read(&resfileref->fileref, sizeof(llen), &llen);
+ if (err)
+ return err;
+
+ /* check that we do not ask to read more data than avalaible */
+ if ((offset + len) > get_UINT32BE(llen))
+ return IMGTOOLERR_UNEXPECTED;
+
+ if (offset)
+ { /* seek to resource data offset in resource data section */
+ err = mac_file_seek(&resfileref->fileref, resfileref->data_offs+data_offs+4+offset);
+ if (err)
+ return err;
+ }
+
+ /* get data */
+ err = mac_file_read(&resfileref->fileref, len, dest);
+ if (err)
+ return err;
+
+ return IMGTOOLERR_SUCCESS;
+}
+#endif
+
+#if 0
+#pragma mark -
+#pragma mark DESKTOP FILE IMPLEMENTATION
+#endif
+/*
+ All macintosh volumes have information stored in the desktop file or the
+ desktop database.
+
+ Such information include file comments, copy of BNDL and FREF resources
+ that describes supported file types for each application on the volume,
+ copy of icons for each file type registered by each application on the
+ volume, etc. On MFS volumes, the list of folders is stored in the desktop
+ file as well.
+
+
+ There have been two implementations of the desktop metadata:
+
+ * The original desktop file. The database is stored in the resource fork
+ of a (usually invisible) file called "Desktop" (case may change
+ according to system versions), located at the root of the volume. The
+ desktop file is used by System 6 and earlier for all volumes (unless
+ Appleshare 2 is installed and the volume is shared IIRC), and by System
+ 7 and later for volumes smaller than 2MBytes (so that floppy disks
+ remain fully compatible with earlier versions of system). The desktop
+ file is incompletely documented by Apple technote TB06.
+
+ * The desktop database. The database is stored in the resource fork is
+ stored in the data fork of two (usually invisible) files called
+ "Desktop DF" and "Desktop DF". The desktop database is used for
+ volumes shared by Appleshare 2, and for most volumes under System 7 and
+ later. The format of these file is not documented AFAIK.
+
+
+ The reasons for the introduction of the desktop database were:
+ * the macintosh resource manager cannot share resource files, which was
+ a problem for Appleshare
+ * the macintosh resource manager is pretty limited (+/-16MByte of data and
+ 2727 resources at most), which was a problem for large hard disks with
+ many programs/comments
+*/
+
+#ifdef UNUSED_FUNCTION
+/*
+ get_comment
+
+ Get a comment from the Desktop file
+
+ l2_img (I): macintosh image the data should be read from
+ id (I): comment id (from mfs_hashString(), or HFS FXInfo/DXInfo records)
+ comment (O): comment that has been read
+
+ Return imgtool error code
+*/
+static imgtoolerr_t get_comment(mac_l2_imgref *l2_img, UINT16 id, mac_str255 comment)
+{
+ static const UINT8 desktop_fname[] = {'\7','D','e','s','k','t','o','p'};
+ #define restype_FCMT (('F' << 24) | ('C' << 16) | ('M' << 8) | 'T')
+ mac_resfileref resfileref;
+ rsrc_ref_entry resentry;
+ UINT32 reslen;
+ imgtoolerr_t err;
+
+ /* open rsrc fork of file Desktop in root directory */
+ err = mac_file_open(l2_img, 2, desktop_fname, rsrc_fork, &resfileref.fileref);
+ if (err)
+ return err;
+
+ /* open resource structures */
+ err = resfile_open(&resfileref);
+ if (err)
+ return err;
+
+ /* look for resource FCMT #id */
+ err = resfile_get_entry(&resfileref, restype_FCMT, id, &resentry);
+ if (err)
+ return err;
+
+ /* extract comment len */
+ err = resfile_get_reslen(&resfileref, &resentry, &reslen);
+ if (err)
+ return err;
+
+ /* check comment len */
+ if (reslen > 256)
+ /* hurk */
+ /*return IMGTOOLERR_CORRUPTIMAGE;*/
+ /* people willing to extend the MFM comment field (you know, the kind
+ of masochists that try to support 20-year-old OSes) might append extra
+ fields, so we just truncate the resource */
+ reslen = 256;
+
+ /* extract comment data */
+ err = resfile_get_resdata(&resfileref, &resentry, 0, reslen, comment);
+ if (err)
+ return err;
+
+ /* phew, we are done! */
+ return IMGTOOLERR_SUCCESS;
+}
+#endif
+
+#if 0
+#pragma mark -
+#pragma mark IMGTOOL MODULE IMPLEMENTATION
+#endif
+
+#ifdef UNUSED_FUNCTION
+static void mac_image_exit(imgtool_image *img);
+#endif
+static void mac_image_info(imgtool_image *img, char *string, size_t len);
+static imgtoolerr_t mac_image_beginenum(imgtool_directory *enumeration, const char *path);
+static imgtoolerr_t mac_image_nextenum(imgtool_directory *enumeration, imgtool_dirent *ent);
+static imgtoolerr_t mac_image_freespace(imgtool_partition *partition, UINT64 *size);
+static imgtoolerr_t mac_image_readfile(imgtool_partition *partition, const char *filename, const char *fork, imgtool_stream *destf);
+static imgtoolerr_t mac_image_writefile(imgtool_partition *partition, const char *filename, const char *fork, imgtool_stream *sourcef, option_resolution *writeoptions);
+
+#ifdef UNUSED_FUNCTION
+/*
+ close a mfs/hfs image
+*/
+static void mac_image_exit(imgtool_image *img)
+{
+ mac_l2_imgref *image = get_imgref(img);
+
+ mac_image_close(image);
+}
+#endif
+
+/*
+ get basic information on a mfs/hfs image
+
+ Currently returns the volume name
+*/
+static void mac_image_info(imgtool_image *img, char *string, size_t len)
+{
+ mac_l2_imgref *image = get_imgref(img);
+
+ switch (image->format)
+ {
+ case L2I_MFS:
+ mac_to_c_strncpy(string, len, image->u.mfs.volname);
+ break;
+
+ case L2I_HFS:
+ mac_to_c_strncpy(string, len, image->u.hfs.volname);
+ break;
+ }
+}
+
+/*
+ MFS/HFS catalog iterator, used when imgtool reads the catalog
+*/
+typedef struct mac_iterator
+{
+ mac_format format;
+ mac_l2_imgref *l2_img;
+ union
+ {
+ struct
+ {
+ mfs_dirref dirref; /* open directory reference */
+ } mfs;
+ struct
+ {
+ hfs_cat_enumerator catref; /* catalog file enumerator */
+ } hfs;
+ } u;
+} mac_iterator;
+
+/*
+ Open the disk catalog for enumeration
+*/
+static imgtoolerr_t mac_image_beginenum(imgtool_directory *enumeration, const char *path)
+{
+ mac_l2_imgref *image = get_imgref(imgtool_directory_image(enumeration));
+ mac_iterator *iter = (mac_iterator *) imgtool_directory_extrabytes(enumeration);
+ imgtoolerr_t err = IMGTOOLERR_UNEXPECTED;
+
+ iter->format = image->format;
+ iter->l2_img = image;
+
+ switch (iter->format)
+ {
+ case L2I_MFS:
+ err = mfs_dir_open(image, path, &iter->u.mfs.dirref);
+ break;
+
+ case L2I_HFS:
+ err = hfs_cat_open(image, path, &iter->u.hfs.catref);
+ break;
+ }
+
+ if (err)
+ return err;
+
+ return IMGTOOLERR_SUCCESS;
+}
+
+/*
+ Enumerate disk catalog next entry (MFS)
+*/
+static imgtoolerr_t mfs_image_nextenum(mac_iterator *iter, imgtool_dirent *ent)
+{
+ mfs_dir_entry *cur_dir_entry;
+ imgtoolerr_t err;
+
+
+ assert(iter->format == L2I_MFS);
+
+ ent->corrupt = 0;
+ ent->eof = 0;
+
+ err = mfs_dir_read(&iter->u.mfs.dirref, &cur_dir_entry);
+ if (err)
+ {
+ /* error */
+ ent->corrupt = 1;
+ return err;
+ }
+ else if (!cur_dir_entry)
+ {
+ /* EOF */
+ ent->eof = 1;
+ return IMGTOOLERR_SUCCESS;
+ }
+
+ /* copy info */
+ mac_to_c_strncpy(ent->filename, ARRAY_LENGTH(ent->filename), cur_dir_entry->name);
+ ent->filesize = get_UINT32BE(cur_dir_entry->dataPhysicalSize)
+ + get_UINT32BE(cur_dir_entry->rsrcPhysicalSize);
+
+ return IMGTOOLERR_SUCCESS;
+}
+
+#if 0
+/*
+ Concatenate path elements in the reverse order
+
+ dest (O): destination buffer
+ dest_cur_pos (I/O): current position in destination buffer (buffer is
+ filled from end to start)
+ dest_max_len (I): length of destination buffer (use length minus one if you
+ want to preserve a trailing NUL character)
+ src (I): source C string
+*/
+static void concat_fname(char *dest, int *dest_cur_pos, int dest_max_len, const char *src)
+{
+ static const char ellipsis[] = { '.', '.', '.' };
+ int src_len = strlen(src); /* number of chars from src to insert */
+
+ if (src_len <= *dest_cur_pos)
+ {
+ *dest_cur_pos -= src_len;
+ memcpy(dest + *dest_cur_pos, src, src_len);
+ }
+ else
+ {
+ memcpy(dest, src + src_len - *dest_cur_pos, *dest_cur_pos);
+ *dest_cur_pos = 0;
+ memcpy(dest, ellipsis, (sizeof(ellipsis) <= dest_max_len)
+ ? sizeof(ellipsis)
+ : dest_max_len);
+ }
+}
+#endif
+
+/*
+ Enumerate disk catalog next entry (HFS)
+*/
+static imgtoolerr_t hfs_image_nextenum(mac_iterator *iter, imgtool_dirent *ent)
+{
+ hfs_catKey *catrec_key;
+ hfs_catData *catrec_data;
+ UINT16 dataRecType;
+ imgtoolerr_t err;
+ /* currently, the mac->C conversion transcodes one mac char with at most 3
+ C chars */
+ int cur_name_head;
+
+ assert(iter->format == L2I_HFS);
+
+ ent->corrupt = 0;
+ ent->eof = 0;
+
+ do
+ {
+ err = hfs_cat_read(&iter->u.hfs.catref, &catrec_key, &catrec_data);
+ if (err)
+ {
+ /* error */
+ ent->corrupt = 1;
+ return err;
+ }
+ else if (!catrec_key)
+ {
+ /* EOF */
+ ent->eof = 1;
+ return IMGTOOLERR_SUCCESS;
+ }
+ dataRecType = get_UINT16BE(catrec_data->dataType);
+ } while (((dataRecType != hcrt_Folder) && (dataRecType != hcrt_File))
+ || (get_UINT32BE(catrec_key->parID) != iter->u.hfs.catref.parID));
+
+ /* copy info */
+ switch (get_UINT16BE(catrec_data->dataType))
+ {
+ case hcrt_Folder:
+ ent->directory = 1;
+ ent->filesize = 0;
+ break;
+
+ case hcrt_File:
+ ent->directory = 0;
+ ent->filesize = get_UINT32BE(catrec_data->file.dataPhysicalSize)
+ + get_UINT32BE(catrec_data->file.rsrcPhysicalSize);
+ break;
+ }
+
+ /* initialize file path buffer */
+ cur_name_head = ARRAY_LENGTH(ent->filename);
+ if (cur_name_head > 0)
+ {
+ cur_name_head--;
+ ent->filename[cur_name_head] = '\0';
+ }
+
+ /* insert folder/file name in buffer */
+ mac_to_c_strncpy(ent->filename, ARRAY_LENGTH(ent->filename), catrec_key->cName);
+// concat_fname(ent->filename, &cur_name_head, ARRAY_LENGTH(ent->filename) - 1, buf);
+
+#if 0
+ /* extract parent directory ID */
+ parID = get_UINT32BE(catrec_key->parID);
+
+ /* looping while (parID != 1) will display the volume name; looping while
+ (parID != 2) won't */
+ while (parID != /*1*/2)
+ {
+ /* search catalog for folder thread */
+ err = hfs_cat_search(iter->l2_img, parID, mac_empty_str, &catrec_key, &catrec_data);
+ if (err)
+ {
+ /* error */
+ concat_fname(ent->filename, &cur_name_head, ARRAY_LENGTH(ent->filename) - 1, ":");
+ concat_fname(ent->filename, &cur_name_head, ARRAY_LENGTH(ent->filename) - 1, "???");
+
+ memmove(ent->filename, ent->filename+cur_name_head, ARRAY_LENGTH(ent->filename) - cur_name_head);
+ ent->corrupt = 1;
+ return err;
+ }
+
+ dataRecType = get_UINT16BE(catrec_data->dataType);
+
+ if (dataRecType != hcrt_FolderThread)
+ {
+ /* error */
+ concat_fname(ent->filename, &cur_name_head, ARRAY_LENGTH(ent->filename)-1, ":");
+ concat_fname(ent->filename, &cur_name_head, ARRAY_LENGTH(ent->filename)-1, "???");
+
+ memmove(ent->filename, ent->filename+cur_name_head, ARRAY_LENGTH(ent->filename)-cur_name_head);
+ ent->corrupt = 1;
+ return IMGTOOLERR_CORRUPTIMAGE;
+ }
+
+ /* got folder thread record: insert the folder name at the start of
+ file path, then iterate */
+ mac_to_c_strncpy(buf, sizeof(buf), catrec_data->thread.nodeName);
+ concat_fname(ent->filename, &cur_name_head, ARRAY_LENGTH(ent->filename) - 1, ":");
+ concat_fname(ent->filename, &cur_name_head, ARRAY_LENGTH(ent->filename) - 1, buf);
+
+ /* extract parent directory ID */
+ parID = get_UINT32BE(catrec_data->thread.parID);
+ }
+ memmove(ent->filename, ent->filename+cur_name_head, ARRAY_LENGTH(ent->filename) -cur_name_head);
+#endif
+ return IMGTOOLERR_SUCCESS;
+}
+
+/*
+ Enumerate disk catalog next entry
+*/
+static imgtoolerr_t mac_image_nextenum(imgtool_directory *enumeration, imgtool_dirent *ent)
+{
+ imgtoolerr_t err;
+ mac_iterator *iter = (mac_iterator *) imgtool_directory_extrabytes(enumeration);
+
+ switch (iter->format)
+ {
+ case L2I_MFS:
+ err = mfs_image_nextenum(iter, ent);
+ break;
+
+ case L2I_HFS:
+ err = hfs_image_nextenum(iter, ent);
+ break;
+
+ default:
+ assert(1);
+ err = IMGTOOLERR_UNEXPECTED;
+ break;
+ }
+ return err;
+}
+
+/*
+ Compute free space on disk image in bytes
+*/
+static imgtoolerr_t mac_image_freespace(imgtool_partition *partition, UINT64 *size)
+{
+ imgtool_image *image = imgtool_partition_image(partition);
+ *size = ((UINT64) get_imgref(image)->freeABs) * 512;
+ return IMGTOOLERR_SUCCESS;
+}
+
+#ifdef UNUSED_FUNCTION
+static imgtoolerr_t mac_get_comment(mac_l2_imgref *image, mac_str255 filename, const mac_dirent *cat_info, mac_str255 comment)
+{
+ imgtoolerr_t err = IMGTOOLERR_SUCCESS;
+ UINT16 commentID;
+
+ comment[0] = '\0';
+
+ /* get comment from Desktop file */
+ switch (image->format)
+ {
+ case L2I_MFS:
+ commentID = mfs_hashString(filename);
+ err = get_comment(image, commentID, comment);
+ break;
+
+ case L2I_HFS:
+ /* This is the way to get Finder comments in system <= 7. Attached
+ comments use another method, and Finder 8 uses yet another one. */
+ commentID = get_UINT16BE(cat_info->flXFinderInfo.comment);
+ if (commentID)
+ err = get_comment(image, commentID, comment);
+ break;
+ }
+ return err;
+}
+#endif
+
+
+/*
+ Extract a file from a disk image.
+*/
+static imgtoolerr_t mac_image_readfile(imgtool_partition *partition, const char *fpath, const char *fork, imgtool_stream *destf)
+{
+ imgtoolerr_t err;
+ imgtool_image *img = imgtool_partition_image(partition);
+ mac_l2_imgref *image = get_imgref(img);
+ UINT32 parID;
+ mac_str255 filename;
+ mac_dirent cat_info;
+ mac_fileref fileref;
+ UINT8 buf[512];
+ UINT32 i, run_len, data_len;
+ mac_fork_t fork_num;
+
+ err = mac_identify_fork(fork, &fork_num);
+ if (err)
+ return err;
+
+ /* resolve path and fetch file info from directory/catalog */
+ err = mac_lookup_path(image, fpath, &parID, filename, &cat_info, FALSE);
+ if (err)
+ return err;
+ if (cat_info.dataRecType != hcrt_File)
+ return IMGTOOLERR_FILENOTFOUND;
+
+ /* open file */
+ err = mac_file_open(image, parID, filename, fork_num ? rsrc_fork : data_fork, &fileref);
+ if (err)
+ return err;
+
+ data_len = fork_num ? cat_info.rsrcLogicalSize : cat_info.dataLogicalSize;
+
+ /* extract DF */
+ i = 0;
+ while(i < data_len)
+ {
+ run_len = MIN(data_len - i, sizeof(buf));
+
+ err = mac_file_read(&fileref, run_len, buf);
+ if (err)
+ return err;
+ if (stream_write(destf, buf, run_len) != run_len)
+ return IMGTOOLERR_WRITEERROR;
+ i += run_len;
+ }
+
+ return IMGTOOLERR_SUCCESS;
+}
+
+/*
+ Add a file to a disk image.
+*/
+static imgtoolerr_t mac_image_writefile(imgtool_partition *partition, const char *fpath, const char *fork, imgtool_stream *sourcef, option_resolution *writeoptions)
+{
+ imgtool_image *img = imgtool_partition_image(partition);
+ mac_l2_imgref *image = get_imgref(img);
+ UINT32 parID;
+ mac_str255 filename;
+ mac_dirent cat_info;
+ mac_fileref fileref;
+ UINT32 fork_len;
+ /*UINT16 commentID;*/
+ /*mac_str255 comment;*/
+ UINT8 buf[512];
+ UINT32 i, run_len;
+ imgtoolerr_t err;
+ mac_fork_t fork_num;
+
+ (void) writeoptions;
+
+ if (image->format == L2I_HFS)
+ return IMGTOOLERR_UNIMPLEMENTED;
+
+ err = mac_identify_fork(fork, &fork_num);
+ if (err)
+ return err;
+
+#if 0
+ if (header.version_old != 0)
+ return IMGTOOLERR_UNIMPLEMENTED;
+#endif
+ /*mac_strcpy(filename, header.filename);*/
+ memset(&cat_info, 0, sizeof(cat_info));
+ set_UINT32BE(&cat_info.flFinderInfo.type, 0x3F3F3F3F);
+ set_UINT32BE(&cat_info.flFinderInfo.creator, 0x3F3F3F3F);
+ fork_len = stream_size(sourcef);
+ /*comment[0] = get_UINT16BE(header.comment_len);*/ /* comment length */
+ /* Next two fields are set to 0 with MFS volumes. IIRC, 0 normally
+ means system script: I don't think MFS stores the file name script code
+ anywhere on disk, so it should be a reasonable approximation. */
+
+ /* create file */
+ /* clear inited flag and file location in window */
+ set_UINT16BE(&cat_info.flFinderInfo.flags, get_UINT16BE(cat_info.flFinderInfo.flags) & ~fif_hasBeenInited);
+ set_UINT16BE(&cat_info.flFinderInfo.location.v, 0);
+ set_UINT16BE(&cat_info.flFinderInfo.location.h, 0);
+
+ /* resolve path and create file */
+ err = mac_lookup_path(image, fpath, &parID, filename, &cat_info, TRUE);
+ if (err)
+ return err;
+
+ /* open file fork */
+ err = mac_file_open(image, parID, filename, (fork_num ? rsrc_fork : data_fork), &fileref);
+ if (err)
+ return err;
+
+ err = mac_file_seteof(&fileref, fork_len);
+ if (err)
+ return err;
+
+ /* extract fork */
+ for (i=0; i<fork_len;)
+ {
+ run_len = fork_len - i;
+ if (run_len > 512)
+ run_len = 512;
+ if (stream_read(sourcef, buf, run_len) != run_len)
+ return IMGTOOLERR_READERROR;
+ err = mac_file_write(&fileref, run_len, buf);
+ if (err)
+ return err;
+ i += run_len;
+ }
+ return IMGTOOLERR_SUCCESS;
+}
+
+
+
+static imgtoolerr_t mac_image_listforks(imgtool_partition *partition, const char *path, imgtool_forkent *ents, size_t len)
+{
+ imgtoolerr_t err;
+ UINT32 parID;
+ mac_str255 filename;
+ mac_dirent cat_info;
+ int fork_num = 0;
+ imgtool_image *img = imgtool_partition_image(partition);
+ mac_l2_imgref *image = get_imgref(img);
+
+ /* resolve path and fetch file info from directory/catalog */
+ err = mac_lookup_path(image, path, &parID, filename, &cat_info, FALSE);
+ if (err)
+ return err;
+ if (cat_info.dataRecType != hcrt_File)
+ return IMGTOOLERR_FILENOTFOUND;
+
+ /* specify data fork */
+ ents[fork_num].type = FORK_DATA;
+ ents[fork_num].forkname[0] = '\0';
+ ents[fork_num].size = cat_info.dataLogicalSize;
+ fork_num++;
+
+ if (cat_info.rsrcLogicalSize > 0)
+ {
+ /* specify the resource fork */
+ ents[fork_num].type = FORK_RESOURCE;
+ strcpy(ents[fork_num].forkname, "RESOURCE_FORK");
+ ents[fork_num].size = cat_info.rsrcLogicalSize;
+ fork_num++;
+ }
+
+ ents[fork_num].type = FORK_END;
+ return IMGTOOLERR_SUCCESS;
+}
+
+
+
+static imgtoolerr_t mac_image_getattrs(imgtool_partition *partition, const char *path, const UINT32 *attrs, imgtool_attribute *values)
+{
+ imgtoolerr_t err;
+ imgtool_image *img = imgtool_partition_image(partition);
+ UINT32 parID;
+ mac_str255 filename;
+ mac_dirent cat_info;
+ mac_l2_imgref *image = get_imgref(img);
+ int i;
+
+ /* resolve path and fetch file info from directory/catalog */
+ err = mac_lookup_path(image, path, &parID, filename, &cat_info, FALSE);
+ if (err)
+ return err;
+ if (cat_info.dataRecType != hcrt_File)
+ return IMGTOOLERR_FILENOTFOUND;
+
+ for (i = 0; attrs[i]; i++)
+ {
+ switch(attrs[i])
+ {
+ case IMGTOOLATTR_INT_MAC_TYPE:
+ values[i].i = get_UINT32BE(cat_info.flFinderInfo.type);
+ break;
+ case IMGTOOLATTR_INT_MAC_CREATOR:
+ values[i].i = get_UINT32BE(cat_info.flFinderInfo.creator);
+ break;
+ case IMGTOOLATTR_INT_MAC_FINDERFLAGS:
+ values[i].i = get_UINT16BE(cat_info.flFinderInfo.flags);
+ break;
+ case IMGTOOLATTR_INT_MAC_COORDX:
+ values[i].i = get_UINT16BE(cat_info.flFinderInfo.location.h);
+ break;
+ case IMGTOOLATTR_INT_MAC_COORDY:
+ values[i].i = get_UINT16BE(cat_info.flFinderInfo.location.v);
+ break;
+ case IMGTOOLATTR_INT_MAC_FINDERFOLDER:
+ values[i].i = get_UINT16BE(cat_info.flFinderInfo.fldr);
+ break;
+ case IMGTOOLATTR_INT_MAC_ICONID:
+ values[i].i = get_UINT16BE(cat_info.flXFinderInfo.iconID);
+ break;
+ case IMGTOOLATTR_INT_MAC_SCRIPTCODE:
+ values[i].i = cat_info.flXFinderInfo.script;
+ break;
+ case IMGTOOLATTR_INT_MAC_EXTENDEDFLAGS:
+ values[i].i = cat_info.flXFinderInfo.XFlags;
+ break;
+ case IMGTOOLATTR_INT_MAC_COMMENTID:
+ values[i].i = get_UINT16BE(cat_info.flXFinderInfo.comment);
+ break;
+ case IMGTOOLATTR_INT_MAC_PUTAWAYDIRECTORY:
+ values[i].i = get_UINT32BE(cat_info.flXFinderInfo.putAway);
+ break;
+
+ case IMGTOOLATTR_TIME_CREATED:
+ values[i].t = mac_crack_time(cat_info.createDate);
+ break;
+ case IMGTOOLATTR_TIME_LASTMODIFIED:
+ values[i].t = mac_crack_time(cat_info.modifyDate);
+ break;
+ }
+ }
+ return IMGTOOLERR_SUCCESS;
+}
+
+
+
+static imgtoolerr_t mac_image_setattrs(imgtool_partition *partition, const char *path, const UINT32 *attrs, const imgtool_attribute *values)
+{
+ imgtoolerr_t err;
+ UINT32 parID;
+ mac_str255 filename;
+ mac_dirent cat_info;
+ imgtool_image *img = imgtool_partition_image(partition);
+ mac_l2_imgref *image = get_imgref(img);
+ int i;
+
+ /* resolve path and fetch file info from directory/catalog */
+ err = mac_lookup_path(image, path, &parID, filename, &cat_info, FALSE);
+ if (err)
+ return err;
+ if (cat_info.dataRecType != hcrt_File)
+ return IMGTOOLERR_FILENOTFOUND;
+
+ for (i = 0; attrs[i]; i++)
+ {
+ switch(attrs[i])
+ {
+ case IMGTOOLATTR_INT_MAC_TYPE:
+ set_UINT32BE(&cat_info.flFinderInfo.type, values[i].i);
+ break;
+ case IMGTOOLATTR_INT_MAC_CREATOR:
+ set_UINT32BE(&cat_info.flFinderInfo.creator, values[i].i);
+ break;
+ case IMGTOOLATTR_INT_MAC_FINDERFLAGS:
+ set_UINT16BE(&cat_info.flFinderInfo.flags, values[i].i);
+ break;
+ case IMGTOOLATTR_INT_MAC_COORDX:
+ set_UINT16BE(&cat_info.flFinderInfo.location.h, values[i].i);
+ break;
+ case IMGTOOLATTR_INT_MAC_COORDY:
+ set_UINT16BE(&cat_info.flFinderInfo.location.v, values[i].i);
+ break;
+ case IMGTOOLATTR_INT_MAC_FINDERFOLDER:
+ set_UINT16BE(&cat_info.flFinderInfo.fldr, values[i].i);
+ break;
+ case IMGTOOLATTR_INT_MAC_ICONID:
+ set_UINT16BE(&cat_info.flXFinderInfo.iconID, values[i].i);
+ break;
+ case IMGTOOLATTR_INT_MAC_SCRIPTCODE:
+ cat_info.flXFinderInfo.script = values[i].i;
+ break;
+ case IMGTOOLATTR_INT_MAC_EXTENDEDFLAGS:
+ cat_info.flXFinderInfo.XFlags = values[i].i;
+ break;
+ case IMGTOOLATTR_INT_MAC_COMMENTID:
+ set_UINT16BE(&cat_info.flXFinderInfo.comment, values[i].i);
+ break;
+ case IMGTOOLATTR_INT_MAC_PUTAWAYDIRECTORY:
+ set_UINT32BE(&cat_info.flXFinderInfo.putAway, values[i].i);
+ break;
+
+ case IMGTOOLATTR_TIME_CREATED:
+ cat_info.createDate = mac_setup_time(values[i].t);
+ break;
+ case IMGTOOLATTR_TIME_LASTMODIFIED:
+ cat_info.modifyDate = mac_setup_time(values[i].t);
+ break;
+ }
+ }
+
+ /* resolve path and fetch file info from directory/catalog */
+ err = mac_lookup_path(image, path, &parID, filename, &cat_info, TRUE);
+ if (err)
+ return err;
+
+ return IMGTOOLERR_SUCCESS;
+}
+
+
+
+/*************************************
+ *
+ * Our very own resource manager
+ *
+ *************************************/
+
+static const void *mac_walk_resources(const void *resource_fork, size_t resource_fork_length,
+ UINT32 resource_type,
+ int (*discriminator)(const void *resource, UINT16 id, UINT32 length, void *param_),
+ void *param, UINT16 *resource_id, UINT32 *resource_length)
+{
+ UINT32 resource_data_offset, resource_data_length;
+ UINT32 resource_map_offset, resource_map_length;
+ UINT32 resource_typelist_count, resource_typelist_offset;
+ UINT32 resource_entry_offset, resource_entry_count;
+ UINT32 i, this_resource_type;
+ UINT32 this_resource_data, this_resource_length;
+ UINT16 this_resource_id;
+ const void *this_resource_ptr;
+
+ if (resource_fork_length < 16)
+ return NULL;
+
+ /* read resource header; its ok if anything past this point fails */
+ resource_data_offset = pick_integer_be(resource_fork, 0, 4);
+ resource_map_offset = pick_integer_be(resource_fork, 4, 4);
+ resource_data_length = pick_integer_be(resource_fork, 8, 4);
+ resource_map_length = pick_integer_be(resource_fork, 12, 4);
+ if ((resource_data_offset + resource_data_length) > resource_fork_length)
+ return NULL;
+ if ((resource_map_offset + resource_map_length) > resource_fork_length)
+ return NULL;
+ if (resource_map_length < 30)
+ return NULL;
+
+ /* read resource map and locate the resource type list */
+ resource_typelist_offset = pick_integer_be(resource_fork,
+ resource_map_offset + 24, 2) + resource_map_offset;
+ if ((resource_typelist_offset + 2) > resource_fork_length)
+ return NULL;
+ resource_typelist_count = pick_integer_be(resource_fork,
+ resource_typelist_offset, 2) + 1;
+ if ((resource_typelist_offset + resource_typelist_count * 16 + 2) > resource_fork_length)
+ return NULL;
+
+ /* scan the resource type list and locate the entries for this type */
+ resource_entry_count = 0;
+ resource_entry_offset = 0;
+ for (i = 0; i < resource_typelist_count; i++)
+ {
+ this_resource_type = pick_integer_be(resource_fork, resource_typelist_offset
+ + (i * 8) + 2 + 0, 4);
+ if (this_resource_type == resource_type)
+ {
+ resource_entry_count = pick_integer_be(resource_fork, resource_typelist_offset
+ + (i * 8) + 2 + 4, 2) + 1;
+ resource_entry_offset = pick_integer_be(resource_fork, resource_typelist_offset
+ + (i * 8) + 2 + 6, 2) + resource_typelist_offset;
+ break;
+ }
+ }
+
+ /* scan the resource entries, and find the correct resource */
+ for (i = 0; i < resource_entry_count; i++)
+ {
+ this_resource_id = pick_integer_be(resource_fork, resource_entry_offset
+ + (i * 12) + 0, 2);
+ this_resource_data = pick_integer_be(resource_fork, resource_entry_offset
+ + (i * 12) + 5, 3) + resource_data_offset;
+ if ((this_resource_data + 4) > resource_fork_length)
+ return NULL;
+
+ /* gauge the length */
+ this_resource_length = pick_integer_be(resource_fork, this_resource_data, 4);
+ this_resource_data += 4;
+ if ((this_resource_data + this_resource_length) > resource_fork_length)
+ return NULL;
+
+ this_resource_ptr = ((const UINT8 *) resource_fork) + this_resource_data;
+ if (discriminator(this_resource_ptr, this_resource_id,
+ this_resource_length, param))
+ {
+ if (resource_length)
+ *resource_length = this_resource_length;
+ if (resource_id)
+ *resource_id = this_resource_id;
+ return this_resource_ptr;
+ }
+ }
+
+ return NULL;
+}
+
+
+
+static int get_resource_discriminator(const void *resource, UINT16 id, UINT32 length, void *param)
+{
+ const UINT16 *id_ptr = (const UINT16 *) param;
+ return id == *id_ptr;
+}
+
+
+
+static const void *mac_get_resource(const void *resource_fork, size_t resource_fork_length,
+ UINT32 resource_type, UINT16 resource_id, UINT32 *resource_length)
+{
+ return mac_walk_resources(resource_fork, resource_fork_length,
+ resource_type, get_resource_discriminator, &resource_id, NULL, resource_length);
+}
+
+
+
+/*************************************
+ *
+ * Custom icons
+ *
+ *************************************/
+
+static int bundle_discriminator(const void *resource, UINT16 id, UINT32 length, void *param)
+{
+ UINT32 this_creator_code = pick_integer_be(resource, 0, 4);
+ UINT32 desired_creator_code = *((UINT32 *) param);
+ return this_creator_code == desired_creator_code;
+}
+
+
+
+static int get_pixel(const UINT8 *src, int width, int height, int bpp,
+ int x, int y)
+{
+ UINT8 byte, mask;
+ int bit_position;
+
+ byte = src[(y * width + x) * bpp / 8];
+ bit_position = 8 - ((x % (8 / bpp)) + 1) * bpp;
+ mask = (1 << bpp) - 1;
+ return (byte >> bit_position) & mask;
+}
+
+
+
+static int load_icon(UINT32 *dest, const void *resource_fork, UINT64 resource_fork_length,
+ UINT32 resource_type, UINT16 resource_id, int width, int height, int bpp,
+ const UINT32 *palette, int has_mask)
+{
+ int success = FALSE;
+ int y, x, color, is_masked;
+ UINT32 pixel;
+ const UINT8 *src;
+ UINT32 resource_length;
+ UINT32 frame_length;
+ UINT32 total_length;
+
+ frame_length = width * height * bpp / 8;
+ total_length = frame_length * (has_mask ? 2 : 1);
+
+ /* attempt to fetch resource */
+ src = (const UINT8*)mac_get_resource(resource_fork, resource_fork_length, resource_type,
+ resource_id, &resource_length);
+ if (src && (resource_length == total_length))
+ {
+ for (y = 0; y < height; y++)
+ {
+ for (x = 0; x < width; x ++)
+ {
+ /* first check mask bit */
+ if (has_mask)
+ is_masked = get_pixel(src + frame_length, width, height, bpp, x, y);
+ else
+ is_masked = dest[y * width + x] >= 0x80000000;
+
+ if (is_masked)
+ {
+ /* mask is ok; check the actual icon */
+ color = get_pixel(src, width, height, bpp, x, y);
+ pixel = palette[color] | 0xFF000000;
+ }
+ else
+ {
+ /* masked out; nothing */
+ pixel = 0;
+ }
+
+ dest[y * width + x] = pixel;
+ }
+ }
+ success = TRUE;
+ }
+ return success;
+}
+
+
+
+static imgtoolerr_t mac_image_geticoninfo(imgtool_partition *partition, const char *path, imgtool_iconinfo *iconinfo)
+{
+ static const UINT32 mac_palette_1bpp[2] = { 0xFFFFFF, 0x000000 };
+
+ static const UINT32 mac_palette_4bpp[16] =
+ {
+ 0xFFFFFF, 0xFCF305, 0xFF6402, 0xDD0806, 0xF20884, 0x4600A5,
+ 0x0000D4, 0x02ABEA, 0x1FB714, 0x006411, 0x562C05, 0x90713A,
+ 0xC0C0C0, 0x808080, 0x404040, 0x000000
+ };
+
+ static const UINT32 mac_palette_8bpp[256] =
+ {
+ 0xFFFFFF, 0xFFFFCC, 0xFFFF99, 0xFFFF66, 0xFFFF33, 0xFFFF00,
+ 0xFFCCFF, 0xFFCCCC, 0xFFCC99, 0xFFCC66, 0xFFCC33, 0xFFCC00,
+ 0xFF99FF, 0xFF99CC, 0xFF9999, 0xFF9966, 0xFF9933, 0xFF9900,
+ 0xFF66FF, 0xFF66CC, 0xFF6699, 0xFF6666, 0xFF6633, 0xFF6600,
+ 0xFF33FF, 0xFF33CC, 0xFF3399, 0xFF3366, 0xFF3333, 0xFF3300,
+ 0xFF00FF, 0xFF00CC, 0xFF0099, 0xFF0066, 0xFF0033, 0xFF0000,
+ 0xCCFFFF, 0xCCFFCC, 0xCCFF99, 0xCCFF66, 0xCCFF33, 0xCCFF00,
+ 0xCCCCFF, 0xCCCCCC, 0xCCCC99, 0xCCCC66, 0xCCCC33, 0xCCCC00,
+ 0xCC99FF, 0xCC99CC, 0xCC9999, 0xCC9966, 0xCC9933, 0xCC9900,
+ 0xCC66FF, 0xCC66CC, 0xCC6699, 0xCC6666, 0xCC6633, 0xCC6600,
+ 0xCC33FF, 0xCC33CC, 0xCC3399, 0xCC3366, 0xCC3333, 0xCC3300,
+ 0xCC00FF, 0xCC00CC, 0xCC0099, 0xCC0066, 0xCC0033, 0xCC0000,
+ 0x99FFFF, 0x99FFCC, 0x99FF99, 0x99FF66, 0x99FF33, 0x99FF00,
+ 0x99CCFF, 0x99CCCC, 0x99CC99, 0x99CC66, 0x99CC33, 0x99CC00,
+ 0x9999FF, 0x9999CC, 0x999999, 0x999966, 0x999933, 0x999900,
+ 0x9966FF, 0x9966CC, 0x996699, 0x996666, 0x996633, 0x996600,
+ 0x9933FF, 0x9933CC, 0x993399, 0x993366, 0x993333, 0x993300,
+ 0x9900FF, 0x9900CC, 0x990099, 0x990066, 0x990033, 0x990000,
+ 0x66FFFF, 0x66FFCC, 0x66FF99, 0x66FF66, 0x66FF33, 0x66FF00,
+ 0x66CCFF, 0x66CCCC, 0x66CC99, 0x66CC66, 0x66CC33, 0x66CC00,
+ 0x6699FF, 0x6699CC, 0x669999, 0x669966, 0x669933, 0x669900,
+ 0x6666FF, 0x6666CC, 0x666699, 0x666666, 0x666633, 0x666600,
+ 0x6633FF, 0x6633CC, 0x663399, 0x663366, 0x663333, 0x663300,
+ 0x6600FF, 0x6600CC, 0x660099, 0x660066, 0x660033, 0x660000,
+ 0x33FFFF, 0x33FFCC, 0x33FF99, 0x33FF66, 0x33FF33, 0x33FF00,
+ 0x33CCFF, 0x33CCCC, 0x33CC99, 0x33CC66, 0x33CC33, 0x33CC00,
+ 0x3399FF, 0x3399CC, 0x339999, 0x339966, 0x339933, 0x339900,
+ 0x3366FF, 0x3366CC, 0x336699, 0x336666, 0x336633, 0x336600,
+ 0x3333FF, 0x3333CC, 0x333399, 0x333366, 0x333333, 0x333300,
+ 0x3300FF, 0x3300CC, 0x330099, 0x330066, 0x330033, 0x330000,
+ 0x00FFFF, 0x00FFCC, 0x00FF99, 0x00FF66, 0x00FF33, 0x00FF00,
+ 0x00CCFF, 0x00CCCC, 0x00CC99, 0x00CC66, 0x00CC33, 0x00CC00,
+ 0x0099FF, 0x0099CC, 0x009999, 0x009966, 0x009933, 0x009900,
+ 0x0066FF, 0x0066CC, 0x006699, 0x006666, 0x006633, 0x006600,
+ 0x0033FF, 0x0033CC, 0x003399, 0x003366, 0x003333, 0x003300,
+ 0x0000FF, 0x0000CC, 0x000099, 0x000066, 0x000033, 0xEE0000,
+ 0xDD0000, 0xBB0000, 0xAA0000, 0x880000, 0x770000, 0x550000,
+ 0x440000, 0x220000, 0x110000, 0x00EE00, 0x00DD00, 0x00BB00,
+ 0x00AA00, 0x008800, 0x007700, 0x005500, 0x004400, 0x002200,
+ 0x001100, 0x0000EE, 0x0000DD, 0x0000BB, 0x0000AA, 0x000088,
+ 0x000077, 0x000055, 0x000044, 0x000022, 0x000011, 0xEEEEEE,
+ 0xDDDDDD, 0xBBBBBB, 0xAAAAAA, 0x888888, 0x777777, 0x555555,
+ 0x444444, 0x222222, 0x111111, 0x000000
+ };
+
+ static const UINT32 attrs[4] =
+ {
+ IMGTOOLATTR_INT_MAC_TYPE,
+ IMGTOOLATTR_INT_MAC_CREATOR,
+ IMGTOOLATTR_INT_MAC_FINDERFLAGS
+ };
+
+ imgtoolerr_t err;
+ imgtool_attribute attr_values[3];
+ UINT32 type_code, creator_code, finder_flags;
+ imgtool_stream *stream = NULL;
+ const void *resource_fork;
+ UINT64 resource_fork_length;
+ const void *bundle;
+ UINT32 bundle_length, pos, fref_pos, icn_pos, i;
+ UINT16 local_id = 0, resource_id;
+ UINT32 fref_bundleentry_length, icn_bundleentry_length;
+ const void *fref;
+ UINT32 resource_length;
+
+ assert((ARRAY_LENGTH(attrs) - 1)
+ == ARRAY_LENGTH(attr_values));
+
+ /* first retrieve type and creator code */
+ err = mac_image_getattrs(partition, path, attrs, attr_values);
+ if (err)
+ goto done;
+ type_code = (UINT32) attr_values[0].i;
+ creator_code = (UINT32) attr_values[1].i;
+ finder_flags = (UINT32) attr_values[2].i;
+
+ /* check the bundle bit; if clear (and the type is not 'APPL'), use the
+ * desktop file */
+ if (!(finder_flags & 0x2000) && (type_code != /* APPL */ 0x4150504C))
+ path = "Desktop\0";
+
+ stream = stream_open_mem(NULL, 0);
+ if (!stream)
+ {
+ err = IMGTOOLERR_SUCCESS;
+ goto done;
+ }
+
+ /* read in the resource fork */
+ err = mac_image_readfile(partition, path, "RESOURCE_FORK", stream);
+ if (err)
+ goto done;
+ resource_fork = stream_getptr(stream);
+ resource_fork_length = stream_size(stream);
+
+ /* attempt to look up the bundle */
+ bundle = mac_walk_resources(resource_fork, resource_fork_length, /* BNDL */ 0x424E444C,
+ bundle_discriminator, &creator_code, NULL, &bundle_length);
+ if (!bundle)
+ goto done;
+
+ /* find the FREF and the icon family */
+ pos = 8;
+ fref_pos = icn_pos = 0;
+ fref_bundleentry_length = icn_bundleentry_length = 0;
+ while((pos + 10) <= bundle_length)
+ {
+ UINT32 this_bundleentry_type = pick_integer_be(bundle, pos + 0, 4);
+ UINT32 this_bundleentry_length = pick_integer_be(bundle, pos + 4, 2) + 1;
+
+ if (this_bundleentry_type == /* FREF */ 0x46524546)
+ {
+ fref_pos = pos;
+ fref_bundleentry_length = this_bundleentry_length;
+ }
+ if (this_bundleentry_type == /* ICN# */ 0x49434E23)
+ {
+ icn_pos = pos;
+ icn_bundleentry_length = this_bundleentry_length;
+ }
+ pos += 6 + this_bundleentry_length * 4;
+ }
+ if (!fref_pos || !icn_pos)
+ goto done;
+
+ /* look up the FREF */
+ for (i = 0; i < fref_bundleentry_length; i++)
+ {
+ local_id = pick_integer_be(bundle, fref_pos + (i * 4) + 6, 2);
+ resource_id = pick_integer_be(bundle, fref_pos + (i * 4) + 8, 2);
+
+ fref = mac_get_resource(resource_fork, resource_fork_length,
+ /* FREF */ 0x46524546, resource_id, &resource_length);
+ if (fref && (resource_length >= 7))
+ {
+ if (pick_integer_be(fref, 0, 4) == type_code)
+ break;
+ }
+ }
+ if (i >= fref_bundleentry_length)
+ goto done;
+
+ /* now look up the icon family */
+ resource_id = 0;
+ for (i = 0; i < icn_bundleentry_length; i++)
+ {
+ if (pick_integer_be(bundle, icn_pos + (i * 4) + 6, 2) == local_id)
+ {
+ resource_id = pick_integer_be(bundle, icn_pos + (i * 4) + 8, 2);
+ break;
+ }
+ }
+ if (i >= icn_bundleentry_length)
+ goto done;
+
+ /* fetch 32x32 icons (ICN#, icl4, icl8) */
+ if (load_icon((UINT32 *) iconinfo->icon32x32, resource_fork, resource_fork_length,
+ /* ICN# */ 0x49434E23, resource_id, 32, 32, 1, mac_palette_1bpp, TRUE))
+ {
+ iconinfo->icon32x32_specified = 1;
+
+ load_icon((UINT32 *) iconinfo->icon32x32, resource_fork, resource_fork_length,
+ /* icl4 */ 0x69636C34, resource_id, 32, 32, 4, mac_palette_4bpp, FALSE);
+ load_icon((UINT32 *) iconinfo->icon32x32, resource_fork, resource_fork_length,
+ /* icl8 */ 0x69636C38, resource_id, 32, 32, 8, mac_palette_8bpp, FALSE);
+ }
+
+ /* fetch 16x16 icons (ics#, ics4, ics8) */
+ if (load_icon((UINT32 *) iconinfo->icon16x16, resource_fork, resource_fork_length,
+ /* ics# */ 0x69637323, resource_id, 16, 16, 1, mac_palette_1bpp, TRUE))
+ {
+ iconinfo->icon16x16_specified = 1;
+
+ load_icon((UINT32 *) iconinfo->icon32x32, resource_fork, resource_fork_length,
+ /* ics4 */ 0x69637334, resource_id, 32, 32, 4, mac_palette_4bpp, FALSE);
+ load_icon((UINT32 *) iconinfo->icon32x32, resource_fork, resource_fork_length,
+ /* ics8 */ 0x69637338, resource_id, 32, 32, 8, mac_palette_8bpp, FALSE);
+ }
+
+done:
+ if (stream)
+ stream_close(stream);
+ return err;
+}
+
+
+
+/*************************************
+ *
+ * File transfer suggestions
+ *
+ *************************************/
+
+static imgtoolerr_t mac_image_suggesttransfer(imgtool_partition *partition, const char *path, imgtool_transfer_suggestion *suggestions, size_t suggestions_length)
+{
+ imgtoolerr_t err;
+ UINT32 parID;
+ mac_str255 filename;
+ mac_dirent cat_info;
+ imgtool_image *img = imgtool_partition_image(partition);
+ mac_l2_imgref *image = get_imgref(img);
+ mac_filecategory_t file_category = MAC_FILECATEGORY_DATA;
+
+ if (path)
+ {
+ /* resolve path and fetch file info from directory/catalog */
+ err = mac_lookup_path(image, path, &parID, filename, &cat_info, FALSE);
+ if (err)
+ return err;
+ if (cat_info.dataRecType != hcrt_File)
+ return IMGTOOLERR_FILENOTFOUND;
+
+ file_category = (cat_info.rsrcLogicalSize > 0) ? MAC_FILECATEGORY_FORKED : MAC_FILECATEGORY_DATA;
+ }
+
+ mac_suggest_transfer(file_category, suggestions, suggestions_length);
+ return IMGTOOLERR_SUCCESS;
+}
+
+
+
+/*************************************
+ *
+ * Module population
+ *
+ *************************************/
+
+static void generic_mac_get_info(const imgtool_class *imgclass, UINT32 state, union imgtoolinfo *info)
+{
+ switch(state)
+ {
+ /* --- the following bits of info are returned as 64-bit signed integers --- */
+ case IMGTOOLINFO_INT_OPEN_IS_STRICT: info->i = 1; break;
+ case IMGTOOLINFO_INT_IMAGE_EXTRA_BYTES: info->i = sizeof(struct mac_l2_imgref); break;
+ case IMGTOOLINFO_INT_DIRECTORY_EXTRA_BYTES: info->i = sizeof(struct mac_iterator); break;
+ case IMGTOOLINFO_INT_PATH_SEPARATOR: info->i = ':'; break;
+
+ /* --- the following bits of info are returned as NULL-terminated strings --- */
+ case IMGTOOLINFO_STR_FILE: strcpy(info->s = imgtool_temp_str(), __FILE__); break;
+ case IMGTOOLINFO_STR_EOLN: strcpy(info->s = imgtool_temp_str(), "\r"); break;
+
+ /* --- the following bits of info are returned as pointers to data or functions --- */
+ case IMGTOOLINFO_PTR_MAKE_CLASS: info->make_class = imgtool_floppy_make_class; break;
+ case IMGTOOLINFO_PTR_CLOSE: /* info->close = mac_image_exit */; break;
+ case IMGTOOLINFO_PTR_INFO: info->info = mac_image_info; break;
+ case IMGTOOLINFO_PTR_BEGIN_ENUM: info->begin_enum = mac_image_beginenum; break;
+ case IMGTOOLINFO_PTR_NEXT_ENUM: info->next_enum = mac_image_nextenum; break;
+ case IMGTOOLINFO_PTR_FREE_SPACE: info->free_space = mac_image_freespace; break;
+ case IMGTOOLINFO_PTR_READ_FILE: info->read_file = mac_image_readfile; break;
+ case IMGTOOLINFO_PTR_LIST_FORKS: info->list_forks = mac_image_listforks; break;
+ case IMGTOOLINFO_PTR_GET_ATTRS: info->get_attrs = mac_image_getattrs; break;
+ case IMGTOOLINFO_PTR_SET_ATTRS: info->set_attrs = mac_image_setattrs; break;
+ case IMGTOOLINFO_PTR_GET_ICON_INFO: info->get_iconinfo = mac_image_geticoninfo; break;
+ case IMGTOOLINFO_PTR_SUGGEST_TRANSFER: info->suggest_transfer = mac_image_suggesttransfer; break;
+ case IMGTOOLINFO_PTR_FLOPPY_FORMAT: info->p = (void *) floppyoptions_apple35_mac; break;
+ }
+}
+
+
+
+void mac_mfs_get_info(const imgtool_class *imgclass, UINT32 state, union imgtoolinfo *info)
+{
+ switch(state)
+ {
+ /* --- the following bits of info are returned as NULL-terminated strings --- */
+ case IMGTOOLINFO_STR_NAME: strcpy(info->s = imgtool_temp_str(), "mac_mfs"); break;
+ case IMGTOOLINFO_STR_DESCRIPTION: strcpy(info->s = imgtool_temp_str(), "Mac MFS Floppy"); break;
+
+ /* --- the following bits of info are returned as pointers to data or functions --- */
+ case IMGTOOLINFO_PTR_FLOPPY_CREATE: info->create = mfs_image_create; break;
+ case IMGTOOLINFO_PTR_FLOPPY_OPEN: info->open = mfs_image_open; break;
+ case IMGTOOLINFO_PTR_WRITE_FILE: info->write_file = mac_image_writefile; break;
+
+ default: generic_mac_get_info(imgclass, state, info); break;
+ }
+}
+
+
+
+void mac_hfs_get_info(const imgtool_class *imgclass, UINT32 state, union imgtoolinfo *info)
+{
+ switch(state)
+ {
+ /* --- the following bits of info are returned as NULL-terminated strings --- */
+ case IMGTOOLINFO_STR_NAME: strcpy(info->s = imgtool_temp_str(), "mac_hfs"); break;
+ case IMGTOOLINFO_STR_DESCRIPTION: strcpy(info->s = imgtool_temp_str(), "Mac HFS Floppy"); break;
+
+ /* --- the following bits of info are returned as pointers to data or functions --- */
+ case IMGTOOLINFO_PTR_FLOPPY_OPEN: info->open = hfs_image_open; break;
+
+ default: generic_mac_get_info(imgclass, state, info); break;
+ }
+}