Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

Windy

macrumors newbie
Original poster
Dec 29, 2007
6
0
Hello everybody! I've recently gotten myself into the tangly world of programming, with the specific purpose of making great Mac programs in mind.

Right now I'm trying to make a program with functionality that I cannot really seem to find any documentation about. What I need it to do is to look at a specific position in a file (the file is specified by the user) and overwrite whatever letters and numbers which are at that position with new numbers and letters.

In C++ and Java there are functions like seek() and write(), and I was wondering if Objective-C and Cocoa have similar functions? Thanks in advance!
 

cazlar

macrumors 6502
Oct 2, 2003
492
11
Sydney, Australia
Hello everybody! I've recently gotten myself into the tangly world of programming, with the specific purpose of making great Mac programs in mind.

Right now I'm trying to make a program with functionality that I cannot really seem to find any documentation about. What I need it to do is to look at a specific position in a file (the file is specified by the user) and overwrite whatever letters and numbers which are at that position with new numbers and letters.

In C++ and Java there are functions like seek() and write(), and I was wondering if Objective-C and Cocoa have similar functions? Thanks in advance!

You could read it in as a NSData object (or NSString if that is better for your data), then edit that (so NSMutableData/NSMutableString actually), and write it back out.

Code:
+ (id)dataWithContentsOfFile:(NSString *)path options:(unsigned)readOptionsMask error:(NSError **)errorPtr;

- (void)replaceBytesInRange:(NSRange)range withBytes:(const void *)bytes;

- (BOOL)writeToFile:(NSString *)path options:(unsigned)writeOptionsMask error:(NSError **)errorPtr;
 

Windy

macrumors newbie
Original poster
Dec 29, 2007
6
0
Thank you very much for pushing me in the right direction, cazlar! However, I have to admit that I'm having slight difficulties putting it all into pratice. If it not too much to ask, some more help would be truly appreciated.

In my program, I have a NSMutableData object called selectedFile, which I have loaded a file into (as you described). What I am now trying to achive is to seek selectedFile and find 29 numbers that start at position 319504 and replace them with my own. The documentation was very sparse with information regarding how to use NSRange, so I feel slightly lost.

As previously said, I'm new to programming, and especially working with binary data. So any help would, again, be greatly appreciated = )
 

cazlar

macrumors 6502
Oct 2, 2003
492
11
Sydney, Australia
NSRange is not like other NS objects like NSString etc. Instead, you make one just by using NSMakeRange (X, Y), where x=start point and y=length,

So you should just go
Code:
NSRange r = NSMakeRange(319504,29);

later, if you need to get the numbers "out" of a NSRange, you can use r.location and r.length to give you 319504 and 29 respectively.

The more tricky thing with what you are doing is getting the bytes you want to sub into it into the right format in a buffer. I'll point you to http://developer.apple.com/document...MutableData.html#//apple_ref/doc/uid/20002150 for an example.
 

HiRez

macrumors 603
Jan 6, 2004
6,265
2,630
Western US
Hey Windy, if you want you can still use the C functions like fopen(), seek(), rewind(), etc. since Objective-C is a superset of C. But it's not very Cocoa-like and I wouldn't go that road. A *long* time ago I started writing a wrapper class for NSMutableData that would simplify what you're describing (I think I was dealing with staight 8-bit ASCII data). For that project, I actually split it into two classes: one that extracted (read) data out of a byte of arrays (with a cursor as you request), and another that modifies (writes into) a data object. I'm not sure why I did it that way but I probably had a reason. I remember there were numerous tricky issues that needed to be handled though, like endian-ness, and certainly now Unicode would be a major issue. I'm going to post those classes here just to give you ideas, but understand they are a complete, muddled mess as you see them (I can't even understand half the things I was doing in there). Stuff is commented out (maybe it doesn't work). At the time Cocoa was very young so lots has probably changed, and I'm sure lots of this code is broken now. Someday I'd like to get around to rewriting it all because I think for projects where you need to do a ton of data manipulation, a wrapper class could really simplify your life. Believe it or not, I did use these classes for a project and it did help.
 

HiRez

macrumors 603
Jan 6, 2004
6,265
2,630
Western US
Data writer header:

Code:
////////////////////////////////////////////////////////////////////////
// RBDataWriter.h
////////////////////////////////////////////////////////////////////////
// A wrapper for an NSMutableData object.
////////////////////////////////////////////////////////////////////////

#import <Foundation/Foundation.h>
#import "RBDataExtractor.h"


@interface RBDataWriter : RBDataExtractor {
	int encoding;
}

// initializers

- (id)initWithMutableData:(NSMutableData*)theData;
+ (RBDataWriter*)writerWithData:(NSMutableData*)theData;

// public

- (void)setChar:(char)newChar;
- (void)setUnsignedChar:(unsigned char)newChar;
- (void)setShort:(short)newShort;
- (void)setUnsignedShort:(unsigned short)newShort;
- (void)setInt:(int)newInt;
- (void)setUnsignedInt:(unsigned int)newInt;
- (void)setLong:(long)newLong;
- (void)setUnsignedLong:(unsigned long)newLong;
- (void)setString:(NSString*)s;

- (void)setChar:(char)newChar atOffset:(int)offset;
- (void)setUnsignedChar:(unsigned char)newChar atOffset:(int)offset;
- (void)setShort:(short)newShort atOffset:(int)offset;
- (void)setUnsignedShort:(unsigned short)newShort atOffset:(int)offset;
- (void)setInt:(int)newInt atOffset:(int)offset;
- (void)setUnsignedInt:(unsigned int)newInt atOffset:(int)offset;
- (void)setLong:(long)newLong atOffset:(int)offset;
- (void)setUnsignedLong:(unsigned long)newLong atOffset:(int)offset;
- (void)setString:(NSString*)s atOffset:(int)offset;

// accessors

- (int)encoding;
- (void)setEncoding:(int)newEncoding;

@end
 

HiRez

macrumors 603
Jan 6, 2004
6,265
2,630
Western US
Data writer implementation:

Code:
////////////////////////////////////////////////////////////////////////
// RBDataWriter.m
////////////////////////////////////////////////////////////////////////
// A wrapper for an NSMutableData object.
////////////////////////////////////////////////////////////////////////

#import "RBDataWriter.h"


@implementation RBDataWriter

////////////////////////////////////////////////////////////////////////
// initializers
////////////////////////////////////////////////////////////////////////

- (id)init {
	return [self initWithData:[NSData data]];
}

- (id)initWithMutableData:(NSMutableData*)theData {
	if (self = [super init]) {
		[self setData:theData];
		[self setEncoding:NSUTF8StringEncoding];
	}
	return self;
}

+ (RBDataWriter*)writerWithData:(NSMutableData*)theData {
	RBDataWriter *result = [[self alloc] initWithData:theData];
	[result setEncoding:NSUTF8StringEncoding];
	return [result autorelease];
}

- (void)dealloc {
	[super dealloc];
}

////////////////////////////////////////////////////////////////////////
// setting primitive types at the current index cursor location
////////////////////////////////////////////////////////////////////////

- (void)setChar:(char)newChar {
	NSRange range = { [self index], SIZE_CHAR };

	NS_DURING
		[data replaceBytesInRange:range withBytes:&newChar];
	NS_HANDLER
//		NSLog(@"RBDataWriter:setChar: caught exception: %@", [localException name]);
		[localException raise];
	NS_ENDHANDLER

	[self skip:SIZE_CHAR];
}

- (void)setUnsignedChar:(unsigned char)newChar {
	[self setChar:(char)newChar];
}

- (void)setShort:(short)newShort {
	NSRange range = { [self index], SIZE_SHORT };

	NS_DURING
		[data replaceBytesInRange:range withBytes:&newShort];
	NS_HANDLER
//		NSLog(@"RBDataWriter:setShort: caught exception: %@", [localException name]);
		[localException raise];
	NS_ENDHANDLER

	[self skip:SIZE_SHORT];
}

- (void)setUnsignedShort:(unsigned short)newShort {
	[self setShort:(short)newShort];
}

- (void)setInt:(int)newInt {
	NSRange range = { [self index], SIZE_INT };

	NS_DURING
		[data replaceBytesInRange:range withBytes:&newInt];
	NS_HANDLER
//		NSLog(@"RBDataWriter:setInt: caught exception: %@", [localException name]);
		[localException raise];
	NS_ENDHANDLER

	[self skip:SIZE_INT];
}

- (void)setUnsignedInt:(unsigned int)newInt {
	[self setInt:(int)newInt];
}

- (void)setLong:(long)newLong {
	NSRange range = { [self index], SIZE_LONG };

	NS_DURING
		[data replaceBytesInRange:range withBytes:&newLong];
	NS_HANDLER
//		NSLog(@"RBDataWriter:setLong: caught exception: %@", [localException name]);
		[localException raise];
	NS_ENDHANDLER

	[self skip:SIZE_LONG];
}

- (void)setUnsignedLong:(unsigned long)newLong {
	[self setLong:(long)newLong];
}

/*
- (float)setFloat {
	unsigned char buf[SIZE_FLOAT];
	NSRange range = { [self index], SIZE_FLOAT };

	NS_DURING
		[data setBytes:&buf range:range];
	NS_HANDLER
//		NSLog(@"RBDataWriter:setFloat: caught exception: %@", [localException name]);
		[localException raise];
		return (float)0.0;
	NS_ENDHANDLER

	[self skip:SIZE_FLOAT];
	if ([self endian] == ENDIAN_FLIPPED) {
		return (float)((buf[1] & 0xFF) << 24 | (buf[0] & 0xFF) << 16 | (buf[3] & 0xFF) << 8 | (buf[2] & 0xFF));
	} else {
		return (float)((buf[0] & 0xFF) << 24 | (buf[1] & 0xFF) << 16 | (buf[2] & 0xFF) << 8 | (buf[3] & 0xFF));
	}
}

- (double)setDouble {
	unsigned char buf[SIZE_DOUBLE];
	NSRange range = { [self index], SIZE_DOUBLE };

	NS_DURING
		[data setBytes:&buf range:range];
	NS_HANDLER
//		NSLog(@"RBDataWriter:setDouble: caught exception: %@", [localException name]);
		[localException raise];
		return (double)0.0;
	NS_ENDHANDLER

	[self skip:SIZE_DOUBLE];
	if ([self endian] == ENDIAN_FLIPPED) {
		return (double)((buf[1] & 0xFF) << 56 | (buf[0] & 0xFF) << 48 | (buf[3] & 0xFF) << 40 | (buf[2] & 0xFF) << 32 | (buf[5] & 0xFF) << 24 | (buf[4] & 0xFF) << 16 | (buf[7] & 0xFF) << 8 | (buf[6] & 0xFF));
	} else {
		return (double)((buf[0] & 0xFF) << 56 | (buf[1] & 0xFF) << 48 | (buf[2] & 0xFF) << 40 | (buf[3] & 0xFF) << 32 | (buf[4] & 0xFF) << 24 | (buf[5] & 0xFF) << 16 | (buf[6] & 0xFF) << 8 | (buf[7] & 0xFF));
	}
}
*/

- (void)setString:(NSString*)s {
	NSRange r = { index, [s length] };
	NSData *stringData = [s dataUsingEncoding:encoding];
	unsigned char bytes[[s length]];
	
	[stringData getBytes:&bytes];
	[data replaceBytesInRange:r withBytes:&bytes];
}

////////////////////////////////////////////////////////////////////////
// setting primitive types at an arbitrary offset (the index cursor is
// not moved)
////////////////////////////////////////////////////////////////////////

- (void)setChar:(char)newChar atOffset:(int)offset {
	int x = index;
	[self setIndex:offset];
	[self setChar:newChar];
	[self setIndex:x];
}

- (void)setUnsignedChar:(unsigned char)newChar atOffset:(int)offset {
	[self setChar:(char)newChar atOffset:offset];
}

- (void)setShort:(short)newShort atOffset:(int)offset {
	int x = index;
	[self setIndex:offset];
	[self setShort:newShort];
	[self setIndex:x];
}

- (void)setUnsignedShort:(unsigned short)newShort atOffset:(int)offset {
	[self setShort:(short)newShort atOffset:offset];
}

- (void)setInt:(int)newInt atOffset:(int)offset {
	int x = index;
	[self setIndex:offset];
	[self setInt:newInt];
	[self setIndex:x];
}

- (void)setUnsignedInt:(unsigned int)newInt atOffset:(int)offset {
	[self setInt:(int)newInt atOffset:offset];
}

- (void)setLong:(long)newLong atOffset:(int)offset {
	int x = index;
	[self setIndex:offset];
	[self setChar:newLong];
	[self setIndex:x];
}

- (void)setUnsignedLong:(unsigned long)newLong atOffset:(int)offset {
	[self setLong:(long)newLong atOffset:offset];
}

- (void)setString:(NSString*)s atOffset:(int)offset {
	int tempIndex = [self index];
	[self setIndex:offset];
	[self setString:s];
	[self setIndex:tempIndex];
}

/*
- (float)setFloatAtOffset:(int)offset {
	float result;
	int x = index;
	[self setIndex:offset];
	result = [self setFloat];
	[self setIndex:x];
	return result;
}

- (double)setDoubleAtOffset:(int)offset {
	double result;
	int x = index;
	[self setIndex:offset];
	result = [self setDouble];
	[self setIndex:x];
	return result;
}

- (NSPoint)setPointFromShortsAtOffset:(int)offset {
	NSPoint result;
	int x = index;
	[self setIndex:offset];
	result = [self setPointFromShorts];
	[self setIndex:x];
	return result;
}

- (NSPoint)setPointFromUnsignedShortsAtOffset:(int)offset {
	NSPoint result;
	int x = index;
	[self setIndex:offset];
	result = [self setPointFromUnsignedShorts];
	[self setIndex:x];
	return result;	
}

- (NSPoint)setPointFromIntsAtOffset:(int)offset {
	NSPoint result;
	int x = index;
	[self setIndex:offset];
	result = [self setPointFromInts];
	[self setIndex:x];
	return result;
}

- (NSPoint)setPointFromUnsignedIntsAtOffset:(int)offset {
	NSPoint result;
	int x = index;
	[self setIndex:offset];
	result = [self setPointFromUnsignedInts];
	[self setIndex:x];
	return result;
}

////////////////////////////////////////////////////////////////////////
// setting structures and objects
////////////////////////////////////////////////////////////////////////

- (NSPoint)setPointFromShorts {
	return NSMakePoint([self setShort], [self setShort]);
}

- (NSPoint)setPointFromUnsignedShorts {
	return NSMakePoint([self setUnsignedShort], [self setUnsignedShort]);
}

- (NSPoint)setPointFromInts {
	return NSMakePoint([self setInt], [self setInt]);
}

- (NSPoint)setPointFromUnsignedInts {
	return NSMakePoint([self setUnsignedInt], [self setUnsignedInt]);
}

- (NSString*)setStringOfLength:(int)length {
	unsigned char buf[length];
	NSRange range = { [self index], length }; // temp holder: NSUTF8StringEncoding

	NS_DURING
		[data setBytes:&buf range:range];
//		buf[length - 1] = 0x0;
	NS_HANDLER
		NSLog(@"RBDataWriter:setStringOfLength: caught exception: %@", [localException name]);
		// empty string if error
		return [NSString string];
	NS_ENDHANDLER

	[self skip:length];
	return [NSString stringWithCString:buf length:length];
}

- (NSString*)setStringInRange:(NSRange)range {
	NSString *result;
	int tempMark = [self index];
	[self setIndex:range.location];
	result = [self setStringOfLength:range.length];
	[self setIndex:tempMark];
	return result;
}

- (NSString*)setCString {
	int length = [self findNextChar:0] + 1;
	
//	NSLog(@"### 1. setCString: length = %d", length);
	
	if (length == -1) {
		// empty string if no null-terminated string found
		return [NSString string];
	}
	
//	NSLog(@"### 1a. index = %u", [self index]);
//	length -= [self index] - 1;	
//	NSLog(@"### 2. setCString: length = %d", length);

	if (length > 0) {
		unsigned char buf[length];
		NSRange range = { [self index], length };

		NS_DURING
			[data setBytes:&buf range:range];
		NS_HANDLER
			NSLog(@"RBDataWriter:setCStringAtOffset: caught exception: %@", [localException name]);
			// empty string if error
			return [NSString string];
		NS_ENDHANDLER

		[self setIndex:[self index] + length];
		return [NSString stringWithCString:buf];
	}
	// empty string if no null-terminated string found
	return [NSString string];
}

- (NSString*)setCStringAtOffset:(int)offset {
	NSString *s;
	int tempMark = [self index];
	[self setIndex:offset];
	s = [self setCString];
	[self setIndex:tempMark];
	return s;
}

- (NSString*)setCStringInRange:(NSRange)range {
	return nil;
}

////////////////////////////////////////////////////////////////////////
// cursor control
////////////////////////////////////////////////////////////////////////

- (void)home {
	[self setIndex:0];
}

- (int)index {
	return index;
}

- (void)setIndex:(int)newIndex {
	index = newIndex;
}

- (int)distanceFromEnd {
	return ([self size] - [self index]);
}

- (BOOL)atEnd {
	return ([self size] == [self index]);
}

- (BOOL)skipWouldBePastEnd:(int)length {
	return (([self index] + length) > [self size]);
}

- (void)skip:(int)length {
	if (![self skipWouldBePastEnd:length]) {
		index += length;
	}
}

- (void)skipChars:(int)n {
	[self skip:n * SIZE_CHAR];
}

- (void)skipShorts:(int)n {
	[self skip:n * SIZE_SHORT];
}

- (void)skipInts:(int)n {
	[self skip:n * SIZE_INT];
}

- (void)skipLongs:(int)n {
	[self skip:n * SIZE_LONG];
}
- (void)skipFloats:(int)n {
	[self skip:n * SIZE_FLOAT];
}

- (void)skipDoubles:(int)n {
	[self skip:n * SIZE_DOUBLE];
}

- (void)skipObjects:(int)n ofSize:(int)size {
	[self skip:n * size];
}

- (void)mark {
	mark = index;
}

- (void)reset {
	[self setIndex:mark];
}

- (void)markAndReset {
	int temp = mark;
	mark = index;
	[self setIndex:temp];
}

- (int)size {
	return [data length];
}


////////////////////////////////////////////////////////////////////////
// searching
////////////////////////////////////////////////////////////////////////

- (void)skipToNextChar:(char)c {
	char test;
	while (![self atEnd] && ((test = [self setChar]) != c)) {
//		NSLog(@"skipToNextChar: %d(0x%x)", test, test);
	}
}

- (int)findNextChar:(char)c {
	int offset = [self index];
	int length = [self distanceFromEnd];
	int result = 0;
	char test;
	
//	NSLog(@"\nSEARCHING FOR CHARACTER: %d(0x%x)", c, c);
//	NSLog(@"  offset = %d", offset);
//	NSLog(@"  length = %d", length);
	
	while (length - result > 0 && ((test = [self setCharAtOffset:offset + result]) != c)) {
		result++;
//		NSLog(@"findNextChar: %d(0x%x)", test, test);
	}
	
//	NSLog(@"  2. result = %d", result);
//	NSLog(@"  2. length = %d", length);
	
	if (result == length) {
		result = -1; // not found
	}

//	NSLog(@"findNextChar, returning: %d(0x%x)\n", result, result);
	return (int)result;
}


- (void)skipToNextString:(NSString*)s {
}

- (int)findNextString:(NSString*)s {
	return 0;
}

- (NSArray*)arrayOfOffsetsForString:(NSString*)s {
	return nil;
}

////////////////////////////////////////////////////////////////////////
// misc
////////////////////////////////////////////////////////////////////////

- (NSString*)hexDumpString {
	unsigned int i = 0;
	unsigned int j = 0;
	unsigned int k = 0;
	unsigned char temp = 0;
	int perRow = 16;
	BOOL withAsciiSidebar = YES;
	NSMutableString *result = [NSMutableString string];
	
//	NSLog(@"data length = %d", [[self data] length]);
	
	NS_DURING
//		[result appendString:@"\n"];
		// each hex row
		for (i = 0; i <= [[self data] length]; i += perRow) {
			// print hex characters for this row
			for (j = 0; (j < perRow) && (i + j < [[self data] length]); j++) {
				temp = [self setUnsignedCharAtOffset:i + j];
				//	add leading zero if necessary
				if (temp < 0x10) {
					[result appendString:@"0"];
				}
				[result appendFormat:@"%X", temp];
				[result appendString:@" "];
				// add extra spacer every 4 bytes
				if ((((j + 1) % 4) == 0) && j > 0) {
					[result appendString:@" "];
				}
			}

			if (withAsciiSidebar) {
				// tab over to ascii on last hex byte
				for (k = 0; k < ((perRow) - j); k++) {
					[result appendString:@"   "];
					if ((k % 4) == 0) {
						[result appendString:@" "];
					}
				}

				// print ascii characters
				[result appendString:@"    "];
				for (k = 0; (k < perRow) && (i + k < [[self data] length]); k++) {
					temp = [self setUnsignedCharAtOffset:i + k];
					
					if ((temp > 0x20) && (temp < 0x7E)) {
						NSRange r = { i + k, 1 };
						[result appendFormat:@"%@", [self setStringInRange:r]];
					} else {
						[result appendString:@"."];
					}
				}
			}
			[result appendString:@"\n"];
		}
//		[result appendString:@"\n"];
	NS_HANDLER
		NSLog(@"Caught exception in hexDumpString method: %@", [localException name]);
		[localException raise];
	NS_ENDHANDLER
	return result;
}

*/

////////////////////////////////////////////////////////////////////////
// accessors
////////////////////////////////////////////////////////////////////////

- (int)encoding {
	return encoding;
}

- (void)setEncoding:(int)newEncoding {
	encoding = newEncoding;
}

@end
 

HiRez

macrumors 603
Jan 6, 2004
6,265
2,630
Western US
Data extractor header:

Code:
////////////////////////////////////////////////////////////////////////
// RBDataExtractor.h
////////////////////////////////////////////////////////////////////////
// An NSMutableData object with additional convenience methods for
// extracting and mutating various data types and dealing with byte
// ordering.
////////////////////////////////////////////////////////////////////////

#import <Cocoa/Cocoa.h>
#import <stdlib.h>

// optimize these later so they are not evaluated every time
#define SIZE_CHAR		sizeof(char)
#define SIZE_UCHAR		sizeof(unsigned char)
#define SIZE_SHORT		sizeof(short)
#define SIZE_USHORT		sizeof(unsigned short)
#define SIZE_INT		sizeof(int) 
#define SIZE_UINT		sizeof(int)
#define SIZE_LONG		sizeof(long)
#define SIZE_ULONG		sizeof(unsigned long)
#define SIZE_FLOAT		sizeof(float)
#define SIZE_DOUBLE		sizeof(double)

#define ENDIAN_UNFLIPPED	NO
#define ENDIAN_FLIPPED		YES


@interface RBDataExtractor : NSObject {
	unsigned	index;
	unsigned	mark;
	id			data;
	BOOL		endian;
}

// initializers
+ (RBDataExtractor*)extractorWithData:(NSData*)theData;
- (id)init;
- (id)initWithData:(NSData*)theData;

// byte ordering
- (BOOL)endian;
- (void)setEndian:(BOOL)newEndian;

// getting primitive types at the current index cursor location
- (char)getChar;
- (unsigned char)getUnsignedChar;
- (short)getShort;
- (unsigned short)getUnsignedShort;
- (int)getInt;
- (int)getUnsignedInt;
- (long)getLong;
- (unsigned long)getUnsignedLong;
- (float)getFloat;
- (double)getDouble;

// getting primitive types at an arbitrary offset (the index cursor is not moved)
- (char)getCharAtOffset:(int)offset;
- (unsigned char)getUnsignedCharAtOffset:(int)offset;
- (short)getShortAtOffset:(int)offset;
- (unsigned short)getUnsignedShortAtOffset:(int)offset;
- (int)getIntAtOffset:(int)offset;
- (int)getUnsignedIntAtOffset:(int)offset;
- (long)getLongAtOffset:(int)offset;
- (unsigned long)getUnsignedLongAtOffset:(int)offset;
- (float)getFloatAtOffset:(int)offset;
- (double)getDoubleAtOffset:(int)offset;
- (NSPoint)getPointFromShortsAtOffset:(int)offset;
- (NSPoint)getPointFromUnsignedShortsAtOffset:(int)offset;
- (NSPoint)getPointFromIntsAtOffset:(int)offset;
- (NSPoint)getPointFromUnsignedIntsAtOffset:(int)offset;

// getting structures and objects
- (NSPoint)getPointFromShorts;
- (NSPoint)getPointFromUnsignedShorts;
- (NSPoint)getPointFromInts;
- (NSPoint)getPointFromUnsignedInts;
- (NSString*)getStringOfLength:(int)length;
- (NSString*)getStringInRange:(NSRange)range;
- (NSString*)getCString;
- (NSString*)getCStringAtOffset:(int)offset;
- (NSString*)getCStringInRange:(NSRange)range;

// extraction index cursor
- (void)home;
- (int)index;
- (void)setIndex:(int)newIndex;
- (void)skipToEnd;
- (int)distanceFromEnd;
- (BOOL)atEnd;
- (void)skip:(int)length;
- (void)skipChars:(int)n;
- (void)skipShorts:(int)n;
- (void)skipInts:(int)n;
- (void)skipLongs:(int)n;
- (void)skipFloats:(int)n;
- (void)skipDoubles:(int)n;
- (void)skipObjects:(int)n ofSize:(int)size;
- (void)mark;
- (void)reset;
- (void)markAndReset;
- (int)size;

// searching
- (void)skipToNextChar:(char)c;
- (int)findNextChar:(char)c;
- (void)skipToNextString:(NSString*)s;
- (int)findNextString:(NSString*)s;
- (NSArray*)arrayOfOffsetsForString:(NSString*)s;

// misc

- (NSString*)hexDumpString;

// accessors
- (NSData*)data;
- (void)setData:(NSData*)newData;
- (void)setDataWithMutableCopy:(NSMutableData*)newData;

@end
 

HiRez

macrumors 603
Jan 6, 2004
6,265
2,630
Western US
Data extractor implementation:

Code:
////////////////////////////////////////////////////////////////////////
// RBDataExtractor.m
////////////////////////////////////////////////////////////////////////
// An NSMutableData object with additional convenience methods for
// extracting various data types and dealing with byte ordering.
////////////////////////////////////////////////////////////////////////

#import "RBDataExtractor.h"


@implementation RBDataExtractor

////////////////////////////////////////////////////////////////////////
// initializers
////////////////////////////////////////////////////////////////////////

- (id)init {
	return [self initWithData:[NSData data]];
}

- (id)initWithData:(NSData*)theData {
	if (self = [super init]) {
		[self setEndian:ENDIAN_FLIPPED];
		[self setData:theData];
	}
	return self;
}

+ (RBDataExtractor*)extractorWithData:(NSData*)theData {
	RBDataExtractor *result = [[self alloc] initWithData:theData];
	return [result autorelease];
}

- (void)dealloc {
//	NSLog(@"RBDataExtractor dealloc: releasing data with retain count of %d", [data retainCount]);
	[self setData:nil];
	[super dealloc];
}

////////////////////////////////////////////////////////////////////////
// byte ordering
////////////////////////////////////////////////////////////////////////

- (BOOL)endian {
	return endian;
}

- (void)setEndian:(BOOL)newEndian {
	endian = newEndian;
}

////////////////////////////////////////////////////////////////////////
// getting primitive types at the current index cursor location
////////////////////////////////////////////////////////////////////////

- (char)getChar {
	char buf[SIZE_CHAR];
	NSRange range = { [self index], SIZE_CHAR };
	
	NS_DURING
		[data getBytes:&buf range:range];
	NS_HANDLER
		NSLog(@"RBDataExtractor:getChar: caught exception: %@", [localException name]);
		[localException raise];
		return (char)0;
	NS_ENDHANDLER
	
	[self skip:SIZE_CHAR];
	return (buf[0] & 0xFF);
}

- (unsigned char)getUnsignedChar {
	return (unsigned char)[self getChar];
}

- (short)getShort {
	char buf[SIZE_SHORT];
	NSRange range = { [self index], SIZE_SHORT };

	NS_DURING
		[data getBytes:&buf range:range];
	NS_HANDLER
		NSLog(@"RBDataExtractor:getShort: caught exception: %@", [localException name]);
		[localException raise];
		return (short)0;
	NS_ENDHANDLER

	[self skip:SIZE_SHORT];
	if ([self endian] == ENDIAN_FLIPPED) {
		return ((buf[1] & 0xFF) << 8 | (buf[0] & 0xFF));
	} else {
		return ((buf[0] & 0xFF) << 8 | (buf[1] & 0xFF));		
	}
}

- (unsigned short)getUnsignedShort {
	return (unsigned short)[self getShort];
}

- (int)getInt {
	char buf[SIZE_INT];
	NSRange range = { [self index], SIZE_INT };

	NS_DURING
		[data getBytes:&buf range:range];
	NS_HANDLER
		NSLog(@"RBDataExtractor:getInt: caught exception: %@", [localException name]);
		[localException raise];
		return (int)0;
	NS_ENDHANDLER

	[self skip:SIZE_INT];
	if ([self endian] == ENDIAN_FLIPPED) {
		return ((buf[1] & 0xFF) << 24 | (buf[0] & 0xFF) << 16 | (buf[3] & 0xFF) << 8 | (buf[2] & 0xFF));
	} else {
		return ((buf[0] & 0xFF) << 24 | (buf[1] & 0xFF) << 16 | (buf[2] & 0xFF) << 8 | (buf[3] & 0xFF));
	}
}

- (int)getUnsignedInt {
	return (int)[self getInt];
}

- (long)getLong {
	char buf[SIZE_LONG];
	NSRange range = { [self index], SIZE_LONG };

	NS_DURING
		[data getBytes:&buf range:range];
	NS_HANDLER
		NSLog(@"RBDataExtractor:getInt: caught exception: %@", [localException name]);
		[localException raise];
		return (long)0;
	NS_ENDHANDLER

	[self skip:SIZE_LONG];
	if ([self endian] == ENDIAN_FLIPPED) {
		return (long)((buf[1] & 0xFF) << 56 | (buf[0] & 0xFF) << 48 | (buf[3] & 0xFF) << 40 | (buf[2] & 0xFF) << 32 | (buf[5] & 0xFF) << 24 | (buf[4] & 0xFF) << 16 | (buf[7] & 0xFF) << 8 | (buf[6] & 0xFF));
	} else {
		return (long)((buf[0] & 0xFF) << 56 | (buf[1] & 0xFF) << 48 | (buf[2] & 0xFF) << 40 | (buf[3] & 0xFF) << 32 | (buf[4] & 0xFF) << 24 | (buf[5] & 0xFF) << 16 | (buf[6] & 0xFF) << 8 | (buf[7] & 0xFF));
	}
}

- (unsigned long)getUnsignedLong {
	return (unsigned long)[self getLong];
}

- (float)getFloat {
	char buf[SIZE_FLOAT];
	NSRange range = { [self index], SIZE_FLOAT };

	NS_DURING
		[data getBytes:&buf range:range];
	NS_HANDLER
		NSLog(@"RBDataExtractor:getFloat: caught exception: %@", [localException name]);
		[localException raise];
		return (float)0.0;
	NS_ENDHANDLER

	[self skip:SIZE_FLOAT];
	if ([self endian] == ENDIAN_FLIPPED) {
		return (float)((buf[1] & 0xFF) << 24 | (buf[0] & 0xFF) << 16 | (buf[3] & 0xFF) << 8 | (buf[2] & 0xFF));
	} else {
		return (float)((buf[0] & 0xFF) << 24 | (buf[1] & 0xFF) << 16 | (buf[2] & 0xFF) << 8 | (buf[3] & 0xFF));
	}
}

- (double)getDouble {
	char buf[SIZE_DOUBLE];
	NSRange range = { [self index], SIZE_DOUBLE };

	NS_DURING
		[data getBytes:&buf range:range];
	NS_HANDLER
		NSLog(@"RBDataExtractor:getDouble: caught exception: %@", [localException name]);
		[localException raise];
		return (double)0.0;
	NS_ENDHANDLER

	[self skip:SIZE_DOUBLE];
	if ([self endian] == ENDIAN_FLIPPED) {
		return (double)((buf[1] & 0xFF) << 56 | (buf[0] & 0xFF) << 48 | (buf[3] & 0xFF) << 40 | (buf[2] & 0xFF) << 32 | (buf[5] & 0xFF) << 24 | (buf[4] & 0xFF) << 16 | (buf[7] & 0xFF) << 8 | (buf[6] & 0xFF));
	} else {
		return (double)((buf[0] & 0xFF) << 56 | (buf[1] & 0xFF) << 48 | (buf[2] & 0xFF) << 40 | (buf[3] & 0xFF) << 32 | (buf[4] & 0xFF) << 24 | (buf[5] & 0xFF) << 16 | (buf[6] & 0xFF) << 8 | (buf[7] & 0xFF));
	}
}

////////////////////////////////////////////////////////////////////////
// getting primitive types at an arbitrary offset (the index cursor is
// not moved)
////////////////////////////////////////////////////////////////////////

- (char)getCharAtOffset:(int)offset {
	char result;
	int x = index;
	[self setIndex:offset];
	result = [self getChar];
	[self setIndex:x];
	return result;
}

- (unsigned char)getUnsignedCharAtOffset:(int)offset {
	unsigned char result;
	int x = index;
	[self setIndex:offset];
	result = [self getUnsignedChar];
	[self setIndex:x];
	return result;
}

- (short)getShortAtOffset:(int)offset {
	short result;
	int x = index;
	[self setIndex:offset];
	result = [self getShort];
	[self setIndex:x];
	return result;
}

- (unsigned short)getUnsignedShortAtOffset:(int)offset {
	unsigned short result;
	int x = index;
	[self setIndex:offset];
	result = [self getUnsignedShort];
	[self setIndex:x];
	return result;
}

- (int)getIntAtOffset:(int)offset {
	int result;
	int x = index;
	[self setIndex:offset];
	result = [self getInt];
	[self setIndex:x];
	return result;
}

- (int)getUnsignedIntAtOffset:(int)offset {
	int result;
	int x = index;
	[self setIndex:offset];
	result = [self getUnsignedInt];
	[self setIndex:x];
	return result;
}

- (long)getLongAtOffset:(int)offset {
	long result;
	int x = index;
	[self setIndex:offset];
	result = [self getLong];
	[self setIndex:x];
	return result;
}

- (unsigned long)getUnsignedLongAtOffset:(int)offset {
	unsigned long result;
	int x = index;
	[self setIndex:offset];
	result = [self getUnsignedLong];
	[self setIndex:x];
	return result;
}

- (float)getFloatAtOffset:(int)offset {
	float result;
	int x = index;
	[self setIndex:offset];
	result = [self getFloat];
	[self setIndex:x];
	return result;
}

- (double)getDoubleAtOffset:(int)offset {
	double result;
	int x = index;
	[self setIndex:offset];
	result = [self getDouble];
	[self setIndex:x];
	return result;
}

- (NSPoint)getPointFromShortsAtOffset:(int)offset {
	NSPoint result;
	int x = index;
	[self setIndex:offset];
	result = [self getPointFromShorts];
	[self setIndex:x];
	return result;
}

- (NSPoint)getPointFromUnsignedShortsAtOffset:(int)offset {
	NSPoint result;
	int x = index;
	[self setIndex:offset];
	result = [self getPointFromUnsignedShorts];
	[self setIndex:x];
	return result;	
}

- (NSPoint)getPointFromIntsAtOffset:(int)offset {
	NSPoint result;
	int x = index;
	[self setIndex:offset];
	result = [self getPointFromInts];
	[self setIndex:x];
	return result;
}

- (NSPoint)getPointFromUnsignedIntsAtOffset:(int)offset {
	NSPoint result;
	int x = index;
	[self setIndex:offset];
	result = [self getPointFromUnsignedInts];
	[self setIndex:x];
	return result;
}

////////////////////////////////////////////////////////////////////////
// getting structures and objects
////////////////////////////////////////////////////////////////////////

- (NSPoint)getPointFromShorts {
	return NSMakePoint([self getShort], [self getShort]);
}

- (NSPoint)getPointFromUnsignedShorts {
	return NSMakePoint([self getUnsignedShort], [self getUnsignedShort]);
}

- (NSPoint)getPointFromInts {
	return NSMakePoint([self getInt], [self getInt]);
}

- (NSPoint)getPointFromUnsignedInts {
	return NSMakePoint([self getUnsignedInt], [self getUnsignedInt]);
}

- (NSString*)getStringOfLength:(int)length {
	char buf[length];
	NSRange range = { [self index], length }; // temp holder: NSUTF8StringEncoding

	NS_DURING
		[data getBytes:&buf range:range];
//		buf[length - 1] = 0x0;
	NS_HANDLER
		NSLog(@"RBDataExtractor:getStringOfLength: caught exception: %@", [localException name]);
		// empty string if error
		return [NSString string];
	NS_ENDHANDLER

	[self skip:length];
	return [NSString stringWithCString:buf length:length];
}

- (NSString*)getStringInRange:(NSRange)range {
	NSString *result;
	int tempMark = [self index];
	[self setIndex:range.location];
	result = [self getStringOfLength:range.length];
	[self setIndex:tempMark];
	return result;
}

- (NSString*)getCString {
	int length = [self findNextChar:0] + 1;
	
//	NSLog(@"### 1. getCString: length = %d", length);
	
	if (length == -1) {
		// empty string if no null-terminated string found
		return [NSString string];
	}
	
//	NSLog(@"### 1a. index = %u", [self index]);
//	length -= [self index] - 1;	
//	NSLog(@"### 2. getCString: length = %d", length);

	if (length > 0) {
		char buf[length];
		NSRange range = { [self index], length };

		NS_DURING
			[data getBytes:&buf range:range];
		NS_HANDLER
//			NSLog(@"RBDataExtractor:getCStringAtOffset: caught exception: %@", [localException name]);
			// empty string if error
			return [NSString string];
		NS_ENDHANDLER

		[self setIndex:[self index] + length];
		return [NSString stringWithCString:buf];
	}
	// empty string if no null-terminated string found
	return [NSString string];
}

- (NSString*)getCStringAtOffset:(int)offset {
	NSString *s;
	int tempMark = [self index];
	[self setIndex:offset];
	s = [self getCString];
	[self setIndex:tempMark];
	return s;
}

- (NSString*)getCStringInRange:(NSRange)range {
	return nil;
}

////////////////////////////////////////////////////////////////////////
// cursor control
////////////////////////////////////////////////////////////////////////

- (void)home {
	[self setIndex:0];
}

- (int)index {
	return index;
}

- (void)setIndex:(int)newIndex {
	index = newIndex;
}

- (void)skipToEnd {
	[self setIndex:[self size]];
}

- (int)distanceFromEnd {
	return ([self size] - [self index]);
}

- (BOOL)atEnd {
	return ([self size] == [self index]);
}

- (BOOL)skipWouldBePastEnd:(int)length {
	return (([self index] + length) > [self size]);
}

- (void)skip:(int)length {
	if (![self skipWouldBePastEnd:length]) {
		index += length;
	}
}

- (void)skipChars:(int)n {
	[self skip:n * SIZE_CHAR];
}

- (void)skipShorts:(int)n {
	[self skip:n * SIZE_SHORT];
}

- (void)skipInts:(int)n {
	[self skip:n * SIZE_INT];
}

- (void)skipLongs:(int)n {
	[self skip:n * SIZE_LONG];
}
- (void)skipFloats:(int)n {
	[self skip:n * SIZE_FLOAT];
}

- (void)skipDoubles:(int)n {
	[self skip:n * SIZE_DOUBLE];
}

- (void)skipObjects:(int)n ofSize:(int)size {
	[self skip:n * size];
}

- (void)mark {
	mark = index;
}

- (void)reset {
	[self setIndex:mark];
}

- (void)markAndReset {
	int temp = mark;
	mark = index;
	[self setIndex:temp];
}

- (int)size {
	return [[self data] length];
}


////////////////////////////////////////////////////////////////////////
// searching
////////////////////////////////////////////////////////////////////////

- (void)skipToNextChar:(char)c {
	char test;
	while (![self atEnd] && ((test = [self getChar]) != c)) {
		NSLog(@"skipToNextChar: %d(0x%x)", test, test);
	}
}

- (int)findNextChar:(char)c {
	int offset = [self index];
	int length = [self distanceFromEnd];
	int result = 0;
	char test;
	
//	NSLog(@"\nSEARCHING FOR CHARACTER: %d(0x%x)", c, c);
//	NSLog(@"  offset = %d", offset);
//	NSLog(@"  length = %d", length);
	
	while (length - result > 0 && ((test = [self getCharAtOffset:offset + result]) != c)) {
		result++;
//		NSLog(@"findNextChar: %d(0x%x)", test, test);
	}
	
//	NSLog(@"  2. result = %d", result);
//	NSLog(@"  2. length = %d", length);
	
	if (result == length) {
		result = -1; // not found
	}

//	NSLog(@"findNextChar, returning: %d(0x%x)\n", result, result);
	return (int)result;
}


- (void)skipToNextString:(NSString*)s {
}

- (int)findNextString:(NSString*)s {
	return 0;
}

- (NSArray*)arrayOfOffsetsForString:(NSString*)s {
	return nil;
}

////////////////////////////////////////////////////////////////////////
// misc
////////////////////////////////////////////////////////////////////////

- (NSString*)hexDumpString {
	unsigned int i = 0;
	unsigned int j = 0;
	unsigned int k = 0;
	unsigned char temp = 0;
	int perRow = 16;
	BOOL withAsciiSidebar = YES;
	NSMutableString *result = [NSMutableString string];
	
//	NSLog(@"data length = %d", [[self data] length]);
	
	NS_DURING
//		[result appendString:@"\n"];
		// each hex row
		for (i = 0; i < [[self data] length]; i += perRow) {
			// print hex characters for this row
			for (j = 0; (j < perRow) && (i + j < [[self data] length]); j++) {
				temp = [self getUnsignedCharAtOffset:i + j];
				//	add leading zero if necessary
				if (temp < 0x10) {
					[result appendString:@"0"];
				}
				[result appendFormat:@"%X", temp];
				[result appendString:@" "];
				// add extra spacer every 4 bytes
				if ((((j + 1) % 4) == 0) && j > 0) {
					[result appendString:@" "];
				}
			}

			if (withAsciiSidebar) {
				// tab over to ascii on last hex byte
				for (k = 0; k < ((perRow) - j); k++) {
					[result appendString:@"   "];
					if ((k % 4) == 0) {
						[result appendString:@" "];
					}
				}

				// print ascii characters
				[result appendString:@"    "];
				for (k = 0; (k < perRow) && (i + k < [[self data] length]); k++) {
					temp = [self getUnsignedCharAtOffset:i + k];
					
					if ((temp > 0x20) && (temp < 0x7E)) {
						NSRange r = { i + k, 1 };
						[result appendFormat:@"%@", [self getStringInRange:r]];
					} else {
						[result appendString:@"."];
					}
				}
			}
			[result appendString:@"\n"];
		}
	NS_HANDLER
		NSLog(@"Caught exception in hexDumpString method: %@", [localException name]);
		[localException raise];
	NS_ENDHANDLER
	return result;
}

////////////////////////////////////////////////////////////////////////
// accessors
////////////////////////////////////////////////////////////////////////

- (NSData*)data {
	return data;
}

- (void)setData:(NSData*)newData {
	[newData retain];
	[data release];
	data = newData;
}

- (void)setDataWithMutableCopy:(NSMutableData*)newData {
	[data release];
	data = [newData copy];
}

@end
 

Windy

macrumors newbie
Original poster
Dec 29, 2007
6
0
@ cazlo: I see, that was a lot easier than I have imagined it was. Things are certainly starting to clear up!

@ HiRez: That seems like a rather massive project! It's all a bit too confusing for me at such an early stage, but I'll certainly keep them for future reference. Thanks!

I've followed your directions, and have now ended up with a program that compiles without any errors nor warnings. However, it doesn't seem to do...anything I ask it to. If you have the time, please look through the following snippet and tell me what I might be doing wrong.

Code:
NSString *selectedFile = @"/Users/foo/bar.txt";
		NSMutableData *fileToModify = [[NSMutableData alloc]initWithContentsOfFile:selectedFile];
		
		NSRange patchPosition = NSMakeRange(319504, 29);
		NSMutableArray *bytes = [NSMutableArray array];
		[bytes addObject:@"00"];
		[bytes addObject:@"128"];
		[bytes addObject:@"128"];
		[bytes addObject:@"128"];
		[bytes addObject:@"128"];
		[bytes addObject:@"128"];
		[bytes addObject:@"128"];
		[bytes addObject:@"128"];
		[bytes addObject:@"128"];
		[bytes addObject:@"128"];
		
		[fileToModify replaceBytesInRange:patchPosition withBytes:bytes];
		BOOL success = [fileToModify writeToFile:selectedFile  atomically:TRUE];
		NSLog(@"%d", success);
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.