Talk:DS Store File Format

From MozillaWiki
Jump to navigation Jump to search
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.

The 'pict' structure mentioned in the article is in fact an AliasRecord to the background picture. Dhcmrlchtdj 08:12, 11 October 2007 (PDT)

What follows is a stab at a command-line utility to parse .DS_Store files, partially based on the reverse-engineered information from the article. Dhcmrlchtdj 02:48, 16 October 2007 (PDT)

/*
 *			dsdump.c
 *
 *			Written by Marco Piovanelli, October 2007
 *			<mailto:marco.piovanelli@pobox.com>
 *
 *			compile with:
 *			cc -framework Carbon -o dsdump dsdump.c
 */

#include <stdio.h>
#include <inttypes.h>
#include <Carbon/Carbon.h>

inline uint16_t flip_short(uint16_t x)
{
#if TARGET_RT_LITTLE_ENDIAN
	return ((x & 0xff00) >> 8) |
		   ((x & 0x00ff) << 8);
#else
	return x;
#endif
}

inline uint32_t flip_long(uint32_t x)
{
#if TARGET_RT_LITTLE_ENDIAN
	return ((x & 0xff000000) >> 24) |
		   ((x & 0x00ff0000) >> 8 ) |
		   ((x & 0x0000ff00) << 8 ) |
		   ((x & 0x000000ff) << 24);
#else
	return x;
#endif
}

static void flip_short_array(uint16_t * buf, uint32_t len)
{
#if TARGET_RT_LITTLE_ENDIAN
	int	index;
	for(index = 0; index < len; ++index)
	{
		buf[index] = flip_short(buf[index]);
	}
#endif
}

static void flip_rect(Rect * r)
{
#if TARGET_RT_LITTLE_ENDIAN
	r->top = flip_short(r->top);
	r->left = flip_short(r->left);
	r->bottom = flip_short(r->bottom);
	r->right = flip_short(r->right);
#endif
}

//	type codes

#define			kDSTypeBoolean						'bool'		//	Boolean (1 byte)
#define			kDSTypeShort						'shor'		//	SInt16 (4 bytes, but highest two are always zero)
#define			kDSTypeLong							'long'		//	SInt32 (4 bytes)
#define			kDSTypeOSType						'type'		//	OSType (4 bytes)
#define			kDSTypeUnicodeString				'ustr'		//	UTF-16 encoded string (4-byte length follows type code)
#define			kDSTypeBlob							'blob'		//	arbitrary structure (4-byte length follows type code)

//	data tags

#define			kDSTagWindowInfo					'fwi0'		//	blob -- 16 bytes
#define			kDSTagWindowHeight					'fwvh'		//	shor
#define			kDSTagWindowSidebarWidth			'fwsw'		//	long
#define			kDSTagICSP							'icsp'		//	blob -- 8 bytes
#define			kDSTagIconViewOptions				'icvo'		//	blob -- 26 bytes
#define			kDSTagIconViewTextSize				'icvt'		//	shor
#define			kDSTagIconUnknown1					'icgo'		//	blob -- 8 bytes
#define			kDSTagBackgroundType				'BKGD'		//	blob -- 12 bytes
#define			kDSTagPicture						'pict'		//	blob
#define			kDSTagIconLocation					'Iloc'		//	blob -- 16 bytes
#define			kDSTagDesktopIconLocation			'dilc'		//	blob -- 32 bytes
#define			kDSTagListViewOptions				'lsvo'		//	blob -- 76 bytes
#define			kDSTagListViewTextSize				'lsvt'		//	shor
#define			kDSTagComment						'cmmt'		//	ustr
#define			kDSTagClippingRect					'clip'		//	blob -- 8 bytes
#define			kDSTagViewStyle						'vstl'		//	type -- 4 bytes

//	background types

#define			kDSBackgroundDefault				'DefB'		//	default background (white)
#define			kDSBackgroundColor					'ClrB'		//	color background
#define			kDSBackgroundPicture				'PctB'		//	picture background

//	arrangements

#define			kDSArrangementNone					'none'
#define			kDSArrangementByCreationDate		'ascd'
#define			kDSArrangementByModificationDate	'modd'
#define			kDSArrangementBySize				'phys'
#define			kDSArrangementByKind				'kind'
#define			kDSArrangementByLabel				'labl'

//	label positions

#define			kDSLabelPositionBottom				'botm'
#define			kDSLabelPositionRight				'rght'

//	view styles (Leopard) -- possible values for kDSTagViewStyle

#define			kDSViewStyleIcons					'icnv'
#define			kDSViewStyleList					'Nlsv'
#define			kDSViewStyleColumns					'clmv'
#define			kDSViewStyleCoverFlow				'Flwv'

#pragma options align=mac68k

typedef struct _DSStructHeader
{
	uint32_t	mTag;
	uint32_t	mType;
} DSStructHeader;

typedef struct _DSIconLocation
{
	uint32_t	mLocationX;
	uint32_t	mLocationY;
} DSIconLocation;

typedef struct _DSWindowInfo
{
	Rect		mWindowRect;
} DSWindowInfo;

typedef struct _DSIconViewOptions
{
	uint32_t	mVersion;				//	always 'icv4' in Tiger?
	uint16_t	mIconSize;				//	icon size in pixels
	uint32_t	mArrangement;			//	one of the kDSArrangement* constants
	uint32_t	mLabelPosition;			//	one of the kDSLabelPosition* constants ('botm' or 'rght')
} DSIconViewOptions;

typedef struct _DSBackground
{
	uint32_t	mBackgroundType;
} DSBackground;

#pragma options align=reset

static char * ostype_to_string(uint32_t tag, char string[5])
{
	string[0] = (tag & 0xff000000) >> 24;
	string[1] = (tag & 0x00ff0000) >> 16;
	string[2] = (tag & 0x0000ff00) >> 8;
	string[3] = (tag & 0x000000ff);
	string[4] = 0;

	return string;
}

static void print_cfstring(const char * format, CFStringRef cfstring)
{
	UInt8						nameBuf[32];			//	truncate strings to 31 characters
	CFIndex						usedBufLen = 0;

	//	convert name to ASCII for printf
	CFStringGetBytes(cfstring, CFRangeMake(0, CFStringGetLength(cfstring)), kCFStringEncodingASCII, '*', false, nameBuf, sizeof(nameBuf) - 1, &usedBufLen);
	nameBuf[usedBufLen] = 0;

	printf(format, nameBuf);
}

static void print_rect(const char * format, const Rect * r)
{
	char						rectString[256];

	sprintf(rectString, "[t = %d, l = %d, b = %d, r = %d]", r->top, r->left, r->bottom, r->right);
	printf(format, rectString);
}

static void parse_ds_struct(uint32_t offset, CFStringRef name, uint32_t tag, uint32_t type, const void * dataPtr, uint32_t dataSize)
{
	char						tagString[5];
	char						typeString[5];

	printf("%08x\t", offset);
	print_cfstring("%-32s", name);
	printf("\t%s\t%s\t%5d\t", ostype_to_string(tag, tagString), ostype_to_string(type, typeString), (int)dataSize);

	switch(tag)
	{
		case kDSTagWindowInfo:
		{
			DSWindowInfo		windowInfo = *(const DSWindowInfo*)dataPtr;

			flip_rect(&windowInfo.mWindowRect);
			print_rect("(window_rect = %s)", &windowInfo.mWindowRect);
			break;
		}

		case kDSTagIconViewOptions:
		{
			DSIconViewOptions	iconViewOptions = *(const DSIconViewOptions*)dataPtr;
			const char *		labelPosition = "unknown";
			const char *		arrangement = "unknown";

			iconViewOptions.mIconSize = flip_short(iconViewOptions.mIconSize);
			iconViewOptions.mArrangement = flip_long(iconViewOptions.mArrangement);
			iconViewOptions.mLabelPosition = flip_long(iconViewOptions.mLabelPosition);

			switch(iconViewOptions.mLabelPosition)
			{
				case kDSLabelPositionBottom:		labelPosition = "bottom";	break;
				case kDSLabelPositionRight:			labelPosition = "right";	break;
			}

			switch(iconViewOptions.mArrangement)
			{
				case kDSArrangementNone:				arrangement = "none";					break;
				case kDSArrangementByModificationDate:	arrangement = "by modification date";	break;
				case kDSArrangementByCreationDate:		arrangement = "by creation date";		break;
				case kDSArrangementBySize:				arrangement = "by size";				break;
				case kDSArrangementByKind:				arrangement = "by kind";				break;
				case kDSArrangementByLabel:				arrangement = "by label";				break;
			}

			printf("(icon_size = %d; label_position = %s; arrangement = %s)", iconViewOptions.mIconSize, labelPosition, arrangement);
			break;
		}

		case kDSTagIconViewTextSize:
		{
			printf("(icon_view_text_size = %d)", flip_long(*(const uint32_t*)dataPtr));
			break;
		}
		
		case kDSTagWindowSidebarWidth:
		{
			printf("(sidebar_width = %d)", flip_long(*(const uint32_t*)dataPtr));
			break;
		}

		case kDSTagWindowHeight:
		{
			printf("(window_height = %d)", flip_long(*(const uint32_t*)dataPtr));
			break;
		}

		case kDSTagBackgroundType:
		{
			if(dataSize == 12)
			{
				DSBackground	background = *(const DSBackground *)dataPtr;
				const char *	typeName = 0;

				background.mBackgroundType = flip_long(background.mBackgroundType);
				switch(background.mBackgroundType)
				{
					case kDSBackgroundDefault:		typeName = "default";	break;
					case kDSBackgroundColor:		typeName = "color";		break;
					case kDSBackgroundPicture:		typeName = "picture";	break;
				}

				if(typeName)
				{
					printf("(type = %s)", typeName);
				}
			}
			break;
		}

		case kDSTagPicture:
		{
			Handle				aliasHandle = 0;
			FSRef				fsRef;
			Boolean				wasChanged = false;
			char				path[1024];

			if(PtrToHand(dataPtr, &aliasHandle, dataSize) == noErr)
			{
				if (FSResolveAliasWithMountFlags(0, (AliasHandle)aliasHandle, &fsRef, &wasChanged, kResolveAliasFileNoUI) == noErr)
				{
					if (FSRefMakePath(&fsRef, (UInt8*)path, sizeof(path)) == noErr)
					{
						printf("(alias = %s)", path);
					}
				}
				DisposeHandle(aliasHandle);
			}
			break;
		}

		case kDSTagIconLocation:
		{
			if(dataSize == 16)
			{
				DSIconLocation	iconLocation = *(const DSIconLocation*) dataPtr;

				iconLocation.mLocationX = flip_long(iconLocation.mLocationX);
				iconLocation.mLocationY = flip_long(iconLocation.mLocationY);

				printf("(x = %d; y = %d)", iconLocation.mLocationX, iconLocation.mLocationY);
			}
			break;
		}

		case kDSTagListViewTextSize:
		{
			printf("(list_view_text_size = %d)", flip_long(*(const uint32_t*)dataPtr));
			break;
		}

		case kDSTagComment:
		{
			if(type == kDSTypeUnicodeString)
			{
				uint16_t *		commentBuf = (uint16_t*)dataPtr;
				uint32_t		commentLength = dataSize / 2;
				CFStringRef		comment = 0;

				flip_short_array(commentBuf, commentLength);
				comment = CFStringCreateWithCharacters(kCFAllocatorDefault, commentBuf, commentLength);
				print_cfstring("(comment = %s)", comment);
				CFRelease(comment);
			}
			break;
		}

		case kDSTagClippingRect:
		{
			Rect				clippingRect = * (const Rect*)dataPtr;

			flip_rect(&clippingRect);
			print_rect("(clipping_rect = %s)", &clippingRect);
			break;
		}

		case kDSTagViewStyle:
		{
			if(type == kDSTypeOSType)
			{
				uint32_t		viewStyle = flip_long(*(const uint32_t*)dataPtr);
				const char *	styleName = 0;

				switch(viewStyle)
				{
					case kDSViewStyleIcons:			styleName = "icons";		break;
					case kDSViewStyleList:			styleName = "list";			break;
					case kDSViewStyleColumns:		styleName = "columns";		break;
					case kDSViewStyleCoverFlow:		styleName = "Cover Flow";	break;
				}

				if(styleName)
				{
					printf("(view style = %s)", styleName);
				}
			}
			break;
		}
	}

	printf("\n");
}

static int parse_ds_block(FILE * f, uint32_t blockOffset)
{
	uint32_t					structCount = 0;
	int							structIndex;
	DSStructHeader				dsStruct;

	fseek(f, blockOffset + 8, SEEK_SET);
	if (fread(&structCount, sizeof(structCount), 1, f) != 1)
	{
		return 0;
	}
	structCount = flip_long(structCount);
	if(structCount == 0)
	{
		return 0;
	}

	printf("...block @ %08x contains %d structures...\n", blockOffset, structCount);

	for(structIndex = 0; structIndex < structCount; ++structIndex)
	{
		uint32_t				structOffset = ftell(f);
		uint32_t				objectNameLength = 0;
		uint16_t *				objectNameBuf = 0;
		CFStringRef				objectName = 0;
		uint32_t				dataSize = 0;
		void *					dataBuf = 0;

		//	read object name length
		if(fread(&objectNameLength, sizeof(objectNameLength), 1, f) != 1)
		{
			return;
		}
		objectNameLength = flip_long(objectNameLength);
		objectNameBuf = (uint16_t*)malloc(2 * objectNameLength);

		//	read object name (this is UTF-16 encoded)
		if(fread(objectNameBuf, 2 * objectNameLength, 1, f) != 1)
		{
			return;
		}
		flip_short_array(objectNameBuf, objectNameLength);

		//	create CFString from object name
		objectName = CFStringCreateWithCharacters(kCFAllocatorDefault, objectNameBuf, objectNameLength);
		free(objectNameBuf);

		//	read tag and type
		if(fread(&dsStruct, sizeof(dsStruct), 1, f) != 1)
		{
			return;
		}
		dsStruct.mTag = flip_long(dsStruct.mTag);
		dsStruct.mType = flip_long(dsStruct.mType);

		//	calculate payload size
		switch(dsStruct.mType)
		{
			case kDSTypeBoolean:
			{
				dataSize = 1;
				break;
			}

			case kDSTypeShort:
			case kDSTypeLong:
			case kDSTypeOSType:
			{
				dataSize = 4;
				break;
			}

			case kDSTypeUnicodeString:
			{
				if(fread(&dataSize, sizeof(dataSize), 1, f) != 1)
				{
					return;
				}
				dataSize = flip_long(dataSize);
				dataSize *= 2;
				break;
			}

			case kDSTypeBlob:
			{
				if(fread(&dataSize, sizeof(dataSize), 1, f) != 1)
				{
					return;
				}
				dataSize = flip_long(dataSize);
				break;
			}

			default:
			{
				//	unrecognized data type
				break;
			}
		}

		if(dataSize > 0)
		{
			dataBuf = malloc(dataSize);
			if(fread(dataBuf, dataSize, 1, f) != 1)
			{
				return;
			}
			parse_ds_struct(structOffset, objectName, dsStruct.mTag, dsStruct.mType, dataBuf, dataSize);
			free(dataBuf);
		}

		CFRelease(objectName);
	}

	return structCount;
}

static void parse_ds_store_file(const char * inDSStorePath)
{
	FILE *						f = fopen(inDSStorePath, "r");
	uint32_t					blockOffset;

	if(f)
	{
		printf("%-8s\t%-32s\t%-4s\t%-4s\t%s\t%s\n\n", "offset", "object name", "tag", "type", "length", "description");

		fseek(f, 0x14, SEEK_SET);
		if(fread(&blockOffset, sizeof(blockOffset), 1, f) != 1)
		{
			goto cleanup;
		}
		blockOffset = flip_long(blockOffset);
		blockOffset &= ~15;

		for( ; ; blockOffset = (blockOffset & 0xfffff000) + 0x1000)
		{
			if (parse_ds_block(f, blockOffset) == 0)
			{
				break;
			}
		}

cleanup:
		fclose(f);
	}
}

int main ( int argc, char * argv[] )
{
	parse_ds_store_file(argv[1]);
	return 0 ;
}