XADMaster/CSHandle.m
2019-07-12 17:39:26 +02:00

544 lines
12 KiB
Objective-C

/*
* CSHandle.m
*
* Copyright (c) 2017-present, MacPaw Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#import "CSHandle.h"
#import "CSSubHandle.h"
#include <sys/stat.h>
NSString *CSOutOfMemoryException=@"CSOutOfMemoryException";
NSString *CSEndOfFileException=@"CSEndOfFileException";
NSString *CSNotImplementedException=@"CSNotImplementedException";
NSString *CSNotSupportedException=@"CSNotSupportedException";
@implementation CSHandle
-(id)init
{
if(self=[super init])
{
parent=nil;
bitoffs=-1;
writebyte=0;
writebitsleft=8;
}
return self;
}
-(id)initWithParentHandle:(CSHandle *)parenthandle
{
if(self=[super init])
{
parent=[parenthandle retain];
bitoffs=-1;
writebyte=0;
writebitsleft=8;
}
return self;
}
-(id)initAsCopyOf:(CSHandle *)other
{
if(self=[super init])
{
parent=[other->parent retain];
bitoffs=other->bitoffs;
readbyte=other->readbyte;
readbitsleft=other->readbitsleft;
writebyte=other->writebyte;
writebitsleft=other->writebitsleft;
}
return self;
}
-(void)dealloc
{
[parent release];
[super dealloc];
}
-(void)close {}
//-(off_t)fileSize { [self _raiseNotImplemented:_cmd]; return 0; }
-(off_t)fileSize { return CSHandleMaxLength; }
-(off_t)offsetInFile { [self _raiseNotImplemented:_cmd]; return 0; }
-(BOOL)atEndOfFile { [self _raiseNotImplemented:_cmd]; return NO; }
-(void)seekToFileOffset:(off_t)offs { [self _raiseNotImplemented:_cmd]; }
-(void)seekToEndOfFile { [self _raiseNotImplemented:_cmd]; }
-(void)pushBackByte:(int)byte { [self _raiseNotImplemented:_cmd]; }
-(int)readAtMost:(int)num toBuffer:(void *)buffer { [self _raiseNotImplemented:_cmd]; return 0; }
-(void)writeBytes:(int)num fromBuffer:(const void *)buffer { [self _raiseNotImplemented:_cmd]; }
-(void)skipBytes:(off_t)bytes
{
[self seekToFileOffset:[self offsetInFile]+bytes];
}
-(int8_t)readInt8;
{
int8_t c;
[self readBytes:1 toBuffer:&c];
return c;
}
-(uint8_t)readUInt8
{
uint8_t c;
[self readBytes:1 toBuffer:&c];
return c;
}
#define CSReadValueImpl(type,name,conv) \
-(type)name \
{ \
uint8_t bytes[sizeof(type)]; \
if([self readAtMost:sizeof(type) toBuffer:bytes]!=sizeof(type)) [self _raiseEOF]; \
return conv(bytes); \
}
//CSReadValueImpl(int8_t,readInt8,(int8_t)*)
//CSReadValueImpl(uint8_t,readUInt8,(uint8_t)*)
CSReadValueImpl(int16_t,readInt16BE,CSInt16BE)
CSReadValueImpl(int32_t,readInt32BE,CSInt32BE)
CSReadValueImpl(int64_t,readInt64BE,CSInt64BE)
CSReadValueImpl(uint16_t,readUInt16BE,CSUInt16BE)
CSReadValueImpl(uint32_t,readUInt32BE,CSUInt32BE)
CSReadValueImpl(uint64_t,readUInt64BE,CSUInt64BE)
CSReadValueImpl(int16_t,readInt16LE,CSInt16LE)
CSReadValueImpl(int32_t,readInt32LE,CSInt32LE)
CSReadValueImpl(int64_t,readInt64LE,CSInt64LE)
CSReadValueImpl(uint16_t,readUInt16LE,CSUInt16LE)
CSReadValueImpl(uint32_t,readUInt32LE,CSUInt32LE)
CSReadValueImpl(uint64_t,readUInt64LE,CSUInt64LE)
-(int16_t)readInt16InBigEndianOrder:(BOOL)isbigendian { if(isbigendian) return [self readInt16BE]; else return [self readInt16LE]; }
-(int32_t)readInt32InBigEndianOrder:(BOOL)isbigendian { if(isbigendian) return [self readInt32BE]; else return [self readInt32LE]; }
-(int64_t)readInt64InBigEndianOrder:(BOOL)isbigendian { if(isbigendian) return [self readInt64BE]; else return [self readInt64LE]; }
-(uint16_t)readUInt16InBigEndianOrder:(BOOL)isbigendian { if(isbigendian) return [self readUInt16BE]; else return [self readUInt16LE]; }
-(uint32_t)readUInt32InBigEndianOrder:(BOOL)isbigendian { if(isbigendian) return [self readUInt32BE]; else return [self readUInt32LE]; }
-(uint64_t)readUInt64InBigEndianOrder:(BOOL)isbigendian { if(isbigendian) return [self readUInt64BE]; else return [self readUInt64LE]; }
CSReadValueImpl(uint32_t,readID,CSUInt32BE)
-(uint32_t)readBits:(int)bits
{
int res=0,done=0;
if([self offsetInFile]!=bitoffs) readbitsleft=0;
while(done<bits)
{
if(!readbitsleft)
{
readbyte=[self readUInt8];
bitoffs=[self offsetInFile];
readbitsleft=8;
}
int num=bits-done;
if(num>readbitsleft) num=readbitsleft;
res=(res<<num)|((readbyte>>(readbitsleft-num))&((1<<num)-1));
done+=num;
readbitsleft-=num;
}
return res;
}
-(uint32_t)readBitsLE:(int)bits
{
int res=0,done=0;
if([self offsetInFile]!=bitoffs) readbitsleft=0;
while(done<bits)
{
if(!readbitsleft)
{
readbyte=[self readUInt8];
bitoffs=[self offsetInFile];
readbitsleft=8;
}
int num=bits-done;
if(num>readbitsleft) num=readbitsleft;
res=res|(((readbyte>>(8-readbitsleft))&((1<<num)-1))<<done);
done+=num;
readbitsleft-=num;
}
return res;
}
-(int32_t)readSignedBits:(int)bits
{
uint32_t res=[self readBits:bits];
// return res|((res&(1<<(bits-1)))*0xffffffff);
return -(res&(1<<(bits-1)))|res;
}
-(int32_t)readSignedBitsLE:(int)bits
{
uint32_t res=[self readBitsLE:bits];
return -(res&(1<<(bits-1)))|res;
}
-(void)flushReadBits { readbitsleft=0; }
-(NSData *)readLine
{
int (*readatmost_ptr)(id,SEL,int,void *)=(void *)[self methodForSelector:@selector(readAtMost:toBuffer:)];
NSMutableData *data=[NSMutableData data];
for(;;)
{
uint8_t b[1];
int actual=readatmost_ptr(self,@selector(readAtMost:toBuffer:),1,b);
if(actual==0)
{
if([data length]==0) [self _raiseEOF];
else break;
}
if(b[0]=='\n') break;
[data appendBytes:b length:1];
}
const char *bytes=[data bytes];
long length=[data length];
if(length&&bytes[length-1]=='\r') [data setLength:length-1];
return [NSData dataWithData:data];
}
-(NSString *)readLineWithEncoding:(NSStringEncoding)encoding
{
return [[[NSString alloc] initWithData:[self readLine] encoding:encoding] autorelease];
}
-(NSString *)readUTF8Line
{
return [[[NSString alloc] initWithData:[self readLine] encoding:NSUTF8StringEncoding] autorelease];
}
-(NSData *)fileContents
{
[self seekToFileOffset:0];
return [self remainingFileContents];
}
-(NSData *)remainingFileContents
{
uint8_t buffer[16384];
NSMutableData *data=[NSMutableData data];
int actual;
do
{
actual=[self readAtMost:sizeof(buffer) toBuffer:buffer];
[data appendBytes:buffer length:actual];
}
while(actual!=0);
return [NSData dataWithData:data];
}
-(NSData *)readDataOfLength:(int)length
{
return [[self copyDataOfLength:length] autorelease];
}
-(NSData *)readDataOfLengthAtMost:(int)length
{
return [[self copyDataOfLengthAtMost:length] autorelease];
}
-(NSData *)copyDataOfLength:(int)length
{
NSMutableData *data=[[NSMutableData alloc] initWithLength:length];
if(!data) [self _raiseMemory];
[self readBytes:length toBuffer:[data mutableBytes]];
return data;
}
-(NSData *)copyDataOfLengthAtMost:(int)length
{
NSMutableData *data=[[NSMutableData alloc] initWithLength:length];
if(!data) [self _raiseMemory];
int actual=[self readAtMost:length toBuffer:[data mutableBytes]];
[data setLength:actual];
return data;
}
-(void)readBytes:(int)num toBuffer:(void *)buffer
{
int actual=[self readAtMost:num toBuffer:buffer];
if(actual!=num) [self _raiseEOF];
}
-(off_t)readAndDiscardAtMost:(off_t)num
{
off_t skipped=0;
uint8_t buf[16384];
while(skipped<num)
{
off_t numbytes=num-skipped>sizeof(buf)?sizeof(buf):num-skipped;
int actual=[self readAtMost:(int)numbytes toBuffer:buf];
skipped+=actual;
if(actual==0) break;
}
return skipped;
}
-(void)readAndDiscardBytes:(off_t)num
{
if([self readAndDiscardAtMost:num]!=num) [self _raiseEOF];
}
-(CSHandle *)subHandleOfLength:(off_t)length
{
return [[[CSSubHandle alloc] initWithHandle:[[self copy] autorelease] from:[self offsetInFile] length:length] autorelease];
}
-(CSHandle *)subHandleFrom:(off_t)start length:(off_t)length
{
return [[[CSSubHandle alloc] initWithHandle:[[self copy] autorelease] from:start length:length] autorelease];
}
-(CSHandle *)subHandleToEndOfFileFrom:(off_t)start
{
off_t size=[self fileSize];
if(size==CSHandleMaxLength)
{
return [[[CSSubHandle alloc] initWithHandle:[[self copy] autorelease]
from:start length:CSHandleMaxLength] autorelease];
}
else
{
return [[[CSSubHandle alloc] initWithHandle:[[self copy] autorelease]
from:start length:size-start] autorelease];
}
}
-(CSHandle *)nonCopiedSubHandleOfLength:(off_t)length
{
return [[[CSSubHandle alloc] initWithHandle:self from:[self offsetInFile] length:length] autorelease];
}
-(CSHandle *)nonCopiedSubHandleFrom:(off_t)start length:(off_t)length
{
return [[[CSSubHandle alloc] initWithHandle:self from:start length:length] autorelease];
}
-(CSHandle *)nonCopiedSubHandleToEndOfFileFrom:(off_t)start
{
off_t size=[self fileSize];
if(size==CSHandleMaxLength)
{
return [[[CSSubHandle alloc] initWithHandle:self
from:start length:CSHandleMaxLength] autorelease];
}
else
{
return [[[CSSubHandle alloc] initWithHandle:self
from:start length:size-start] autorelease];
}
}
-(void)writeInt8:(int8_t)val { [self writeBytes:1 fromBuffer:(uint8_t *)&val]; }
-(void)writeUInt8:(uint8_t)val { [self writeBytes:1 fromBuffer:&val]; }
#define CSWriteValueImpl(type,name,conv) \
-(void)name:(type)val \
{ \
uint8_t bytes[sizeof(type)]; \
conv(bytes,val); \
[self writeBytes:sizeof(type) fromBuffer:bytes]; \
}
CSWriteValueImpl(int16_t,writeInt16BE,CSSetInt16BE)
CSWriteValueImpl(int32_t,writeInt32BE,CSSetInt32BE)
//CSWriteValueImpl(int64_t,writeInt64BE,CSSetInt64BE)
CSWriteValueImpl(uint16_t,writeUInt16BE,CSSetUInt16BE)
CSWriteValueImpl(uint32_t,writeUInt32BE,CSSetUInt32BE)
//CSWriteValueImpl(uint64_t,writeUInt64BE,CSSetUInt64BE)
CSWriteValueImpl(int16_t,writeInt16LE,CSSetInt16LE)
CSWriteValueImpl(int32_t,writeInt32LE,CSSetInt32LE)
CSWriteValueImpl(int64_t,writeInt64LE,CSSetInt64LE)
CSWriteValueImpl(uint16_t,writeUInt16LE,CSSetUInt16LE)
CSWriteValueImpl(uint32_t,writeUInt32LE,CSSetUInt32LE)
CSWriteValueImpl(uint64_t,writeUInt64LE,CSSetUInt64LE)
CSWriteValueImpl(uint32_t,writeID,CSSetUInt32BE)
-(void)writeBits:(int)bits value:(uint32_t)val
{
int bitsleft=bits;
while(bitsleft)
{
if(!writebitsleft)
{
[self writeUInt8:writebyte];
writebyte=0;
writebitsleft=8;
}
int num=bitsleft;
if(num>writebitsleft) num=writebitsleft;
writebyte|=((val>>(bitsleft-num))&((1<<num)-1))<<(writebitsleft-num);
bitsleft-=num;
writebitsleft-=num;
}
}
-(void)writeSignedBits:(int)bits value:(int32_t)val;
{
[self writeBits:bits value:val];
}
-(void)flushWriteBits
{
if(writebitsleft!=8) [self writeUInt8:writebyte];
writebyte=0;
writebitsleft=8;
}
-(void)writeData:(NSData *)data
{
[self writeBytes:(int)[data length] fromBuffer:[data bytes]];
}
/*-(void)_raiseClosed
{
[NSException raise:@"CSFileNotOpenException"
format:@"Attempted to read from file \"%@\", which was not open.",name];
}*/
-(void)_raiseMemory
{
[NSException raise:CSOutOfMemoryException
format:@"Out of memory while attempting to read from file \"%@\" (%@).",
[self name],[self class]];
}
-(void)_raiseEOF
{
[NSException raise:CSEndOfFileException
format:@"Attempted to read past the end of file \"%@\" (%@).",
[self name],[self class]];
}
-(void)_raiseNotImplemented:(SEL)selector
{
[NSException raise:CSNotImplementedException
format:@"Attempted to use unimplemented method +[%@ %@] when reading from file \"%@\".",
[self class],NSStringFromSelector(selector),[self name]];
}
-(void)_raiseNotSupported:(SEL)selector
{
[NSException raise:CSNotSupportedException
format:@"Attempted to use unsupported method +[%@ %@] when reading from file \"%@\".",
[self class],NSStringFromSelector(selector),[self name]];
}
-(NSString *)name
{
return [parent name];
}
-(CSHandle *)parentHandle
{
return parent;
}
-(void)setParentHandle:(CSHandle *)newparent
{
[parent autorelease];
parent=[newparent retain];
}
-(NSString *)description
{
if(parent)
{
return [NSString stringWithFormat:@"%@ @ %qu for %@",
[self class],[self offsetInFile],[parent description]];
}
else if([self name])
{
return [NSString stringWithFormat:@"%@ @ %qu for \"%@\"",
[self class],[self offsetInFile],[self name]];
}
else
{
return [NSString stringWithFormat:@"%@ @ %qu",
[self class],[self offsetInFile]];
}
}
-(id)copyWithZone:(NSZone *)zone
{
return [[[self class] allocWithZone:zone] initAsCopyOf:self];
}
@end