Talk:DS Store File Format
From MozillaWiki
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 ; }