Allow the rebase of foreign repositories onto master.

Restores the NuTemplateEditor directory deleted from Git but not from SVN. Foreign repos were forked from SVN tree
This commit is contained in:
Nicholas Shanks 2014-02-20 00:53:03 +00:00
parent 8c3386497a
commit fee2452785
52 changed files with 2902 additions and 4 deletions

38
Cocoa/TEMPLATE EDITOR.txt Normal file
View File

@ -0,0 +1,38 @@
ABOUT THE CODE
--------------
WHAT HAPPENS WHEN A TEMPLATE EDITOR IS OPENED FOR A RESOURCE
The template editor uses a dedicated class for each field type (or family of field types).
These classes are all subclasses of the common base class NuTemplateElement. For things like
lists, there is a subclass NuTemplateGroupElement from which you can instead subclass to
have some of the work done for you.
When opening a resource, the template editor first creates a NuTemplateStream for the template
resource and calls readOneObject: on it until it runs out of data, instantiating a hierarchy
of NuTemplateElement subclasses for based on the template.
After that, it creates a NuTemplateStream for the resource and loops over the template object
hierarchy, creating a copy of each item, and then calling readDataFrom:containingArray: on
the copy, which in turn reads data from the NuTemplateStream and stores it in its instance
variables. For this to work, subclasses of NuTemplateElement *must* implement the NSCopying
protocol.
This results in an object hierarchy that describes the entire resource using template fields
containing the data, which can now be edited, displayed in lists, etc.
WHAT HAPPENS WHEN THE TEMPLATE EDITOR IS CLOSED
As soon as the template editor is closed, another NuTemplateStream is created for the
resource and passed to all template fields (the copies with the resource data, not the
pre-parsed template hierarchy), via writeDataTo:, which is where they should write their
data to the stream. Before that, sizeOnDisk is called on each NuTemplateElement to
calculate the new size of the resource before the actual process of writing them out.
This size must include the size of all of their sub-items, and writeDataTo: must call
these sub items if they are to be written to disk.
SPECIAL CASE: LISTS

View File

@ -0,0 +1,68 @@
#import <Foundation/Foundation.h>
#import "TemplateStream.h"
/*
This is the base class for all template field types. Subclass this to
define a field type of your own.
Note that subclasses *must* implement the NSCopying protocol, which means
if you have instance variables, you must provide your own copyWithZone:
implementation that calls through to the superclass and then copies its
own variables' values (or retains references to them, if that is more
effective and the object in question isn't mutable).
*/
@interface Element : NSObject <NSCopying>
{
BOOL _isTMPL; // for debugging
NSString *type; // Type code of this item (4 chars if from TMPL resource, but we may support longer types later).
NSString *label; // Label ("name") of this field.
NSMutableArray *parentArray; // The NSMutableArray* of the template field containing us, or the template window's list.
}
+ (id)elementForType:(NSString *)type withLabel:(NSString *)label;
- (id)initForType:(NSString *)type withLabel:(NSString *)label;
// This is used to instantiate copies of the item from the template for storing data of the resource. A copy created with this is then sent readDataFrom:.
- (id)copyWithZone:(NSZone *)zone;
// Accessors:
- (void)setIsTMPL:(BOOL)t;
- (BOOL)isTMPL;
- (void)setType:(NSString *)t;
- (NSString *)type;
- (void)setLabel:(NSString *)l;
- (NSString *)label;
- (void)setParentArray:(NSMutableArray *)array;
- (NSMutableArray *)parentArray;
- (NSString *)stringValue; // Used to display your data in the list.
- (BOOL)editable;
// Items that have sub-items (like LSTB, LSTZ, LSTC and other lists) should implement these:
- (int)subElementCount;
- (Element *)subElementAtIndex:(int)n;
- (void)readSubElementsFrom:(TemplateStream *)stream;
// This is called on an item of your class when displaying resource data using a template that uses your field:
- (void)readDataFrom:(TemplateStream *)stream;
// The following are used to write resource data back out:
- (unsigned int)sizeOnDisk;
- (void)writeDataTo:(TemplateStream *)stream;
/* Apart from these messages, a Element may also implement the IBActions for
the standard edit commands (cut, copy, paste, clear). When an element is selected,
the template editor will forward any calls to these items to the element, if it
implements them, and it will automatically enable the appropriate menu items. It
will also forward validateMenuItem:for the Paste menu item to the element.
The createListEntry:action will also be forwarded to elements by the
template editor. Use this to allow creating new list items or for similar
purposes ("Create New Resource..." is renamed to "Create List Entry" while the
template editor is key). */
@end

142
NuTemplateEditor/Element.m Normal file
View File

@ -0,0 +1,142 @@
#import "Element.h"
@implementation Element
+ (id)elementForType:(NSString *)t withLabel:(NSString *)l
{
return [[[self alloc] autorelease] initForType:t withLabel:l];
}
- (id)initForType:(NSString *)t withLabel:(NSString *)l
{
self = [super init];
if(!self) return nil;
label = [l copy];
type = [t copy];
return self;
}
- (void)dealloc
{
[label release];
[type release];
[super dealloc];
}
- (id)copyWithZone:(NSZone *)zone
{
Element *element = [[[self class] allocWithZone:zone] initForType:type withLabel:label];
[element setParentArray:parentArray];
return element;
}
#pragma mark -
- (void)setIsTMPL:(BOOL)t
{
_isTMPL = t;
}
- (BOOL)isTMPL
{
return _isTMPL;
}
- (void)setType:(NSString *)t
{
id old = type;
type = [t copy];
[old release];
}
- (NSString *)type
{
return type;
}
- (void)setLabel:(NSString *)l
{
id old = label;
label = [l copy];
[old release];
}
- (NSString *)label
{
return label;
}
- (void)setParentArray:(NSMutableArray *)array
{
// do not retain parent object
parentArray = array;
}
- (NSMutableArray *)parentArray
{
return parentArray;
}
/*** METHODS SUBCLASSES SHOULD OVERRIDE ***/
- (int)subElementCount
{
// default implementation suitable for most element types
return 0;
}
- (Element *)subElementAtIndex:(int)n
{
// default implementation suitable for most element types
return nil;
}
- (void)readSubElementsFrom:(TemplateStream *)stream
{
// by default, items don't read any sub-elements.
}
// You should read whatever kind of data your template field stands for from "stream"
// and store it in an instance variable.
- (void)readDataFrom:(TemplateStream *)stream
{
NSLog(@"-readDataFrom:called on non-concrete class Element");
}
// Before writeDataTo:is called, this is called to calculate the final resource size:
// Items with sub-elements should return the sum of the sizes of all their sub-elements here as well.
- (unsigned int)sizeOnDisk
{
// default implementation suitable for dimentionless element types
return 0;
}
- (void)writeDataTo:(TemplateStream *)stream
{
NSLog(@"-writeDataTo:called on non-concrete class Element");
}
- (NSString *)stringValue
{
NSLog(@"-stringValue called on non-concrete class Element");
return @"<unknown>";
}
- (void)setStringValue:(NSString *)value
{
// we need this method, otherwise KVC throws an exception which screws up the table
NSLog(@"-setStringValue:@\"%@\" called on non-concrete class Element", value);
}
- (BOOL)editable
{
// override this for non-editable field types
return YES;
}
- (float)rowHeight
{
return 18.0;
}
@end

View File

@ -0,0 +1,12 @@
#import "Element.h"
@interface ElementDATE : Element
{
// seconds since 1 Jan 1904
UInt32 value;
}
- (UInt32)value;
- (void)setValue:(UInt32)v;
- (NSString *)stringValue;
- (void)setStringValue:(NSString *)str;
@end

View File

@ -0,0 +1,64 @@
#import "ElementDATE.h"
@implementation ElementDATE
- (id)copyWithZone:(NSZone *)zone
{
ElementDATE *element = [super copyWithZone:zone];
[element setValue:value];
return element;
}
- (void)readDataFrom:(TemplateStream *)stream
{
[stream readAmount:sizeof(value) toBuffer:&value];
}
- (unsigned int)sizeOnDisk
{
return sizeof(value);
}
- (void)writeDataTo:(TemplateStream *)stream
{
[stream writeAmount:sizeof(value) fromBuffer:&value];
}
- (UInt32)value
{
return value;
}
- (void)setValue:(UInt32)v
{
value = v;
}
- (NSString *)stringValue
{
CFAbsoluteTime cfTime;
OSStatus error = UCConvertSecondsToCFAbsoluteTime(value, &cfTime);
if(error) return nil;
// return [[NSCalendarDate dateWithTimeIntervalSinceReferenceDate:(NSTimeInterval)cfTime] descriptionWithLocale:[NSLocale currentLocale]];
return [[NSCalendarDate dateWithTimeIntervalSinceReferenceDate:(NSTimeInterval)cfTime] descriptionWithLocale:[NSDictionary dictionaryWithObject:[[NSUserDefaults standardUserDefaults] objectForKey:NSShortTimeDateFormatString] forKey:@"NSTimeDateFormatString"]];
// return [[NSCalendarDate dateWithTimeIntervalSinceReferenceDate:(NSTimeInterval)cfTime] descriptionWithLocale:[NSDictionary dictionaryWithObjectsAndKeys:
// [[NSUserDefaults standardUserDefaults] objectForKey:NSShortTimeDateFormatString], @"NSTimeDateFormatString",
// [[NSUserDefaults standardUserDefaults] objectForKey:NSAMPMDesignation], @"NSAMPMDesignation",
// [[NSUserDefaults standardUserDefaults] objectForKey:NSMonthNameArray], @"NSMonthNameArray",
// [[NSUserDefaults standardUserDefaults] objectForKey:NSShortMonthNameArray], @"NSShortMonthNameArray",
// [[NSUserDefaults standardUserDefaults] objectForKey:NSWeekDayNameArray], @"NSWeekDayNameArray",
// [[NSUserDefaults standardUserDefaults] objectForKey:NSShortWeekDayNameArray], @"NSShortWeekDayNameArray",
// nil]];
// return [[NSCalendarDate dateWithTimeIntervalSinceReferenceDate:(NSTimeInterval)cfTime] descriptionWithLocale:[NSDictionary dictionaryWithObject:[[NSCalendar currentCalendar] calendarIdentifier] forKey:@"NSLocaleCalendar"]];
// return [[NSCalendarDate dateWithTimeIntervalSinceReferenceDate:(NSTimeInterval)cfTime] descriptionWithCalendarFormat:[NSCalendar currentCalendar]];// timeZone:[NSTimeZone localTimeZone] locale:[NSDictionary dictionaryWithObject:[NSCalendar currentCalendar] forKey:@"NSLocaleCalendar"]];
}
- (void)setStringValue:(NSString *)str
{
// UCConvertCFAbsoluteTimeToSeconds((CFAbsoluteTime)[[NSCalendarDate dateWithString:str] timeIntervalSinceReferenceDate], &value);
UCConvertCFAbsoluteTimeToSeconds((CFAbsoluteTime)[[NSCalendarDate dateWithNaturalLanguageString:str] timeIntervalSinceReferenceDate], &value);
// UCConvertCFAbsoluteTimeToSeconds((CFAbsoluteTime)[[NSCalendarDate dateWithNaturalLanguageString:str locale:[NSDictionary dictionaryWithObject:[NSLocale currentLocale] forKey:@"NSLocale"]] timeIntervalSinceReferenceDate], &value);
}
@end

View File

@ -0,0 +1,17 @@
#import "Element.h"
@interface ElementDBYT : Element
{
SInt8 value;
}
- (void)setValue:(SInt8)v;
- (SInt8)value;
- (NSString *)stringValue;
- (void)setStringValue:(NSString *)str;
@end
@interface ElementKBYT : ElementDBYT
@end

View File

@ -0,0 +1,53 @@
#import "ElementDBYT.h"
@implementation ElementDBYT
- (id)copyWithZone:(NSZone *)zone
{
ElementDBYT *element = [super copyWithZone:zone];
[element setValue:value];
return element;
}
- (void)readDataFrom:(TemplateStream *)stream
{
[stream readAmount:sizeof(value) toBuffer:&value];
}
- (unsigned int)sizeOnDisk
{
return sizeof(value);
}
- (void)writeDataTo:(TemplateStream *)stream
{
[stream writeAmount:sizeof(value) fromBuffer:&value];
}
- (void)setValue:(SInt8)v
{
value = v;
}
- (SInt8)value
{
return value;
}
- (NSString *)stringValue
{
return [NSString stringWithFormat:@"%hhd", value];
}
- (void)setStringValue:(NSString *)str
{
char cstr[256];
char *endPtr = cstr + 255;
strncpy(cstr, [str cString], 255);
value = strtol(cstr, &endPtr, 10);
}
@end
@implementation ElementKBYT
@end

View File

@ -0,0 +1,14 @@
#import "Element.h"
@interface ElementDLLG : Element
{
SInt64 value;
}
- (void)setValue:(SInt64)v;
- (SInt64)value;
- (NSString *)stringValue;
- (void)setStringValue:(NSString *)str;
@end

View File

@ -0,0 +1,50 @@
#import "ElementDLLG.h"
@implementation ElementDLLG
- (id)copyWithZone:(NSZone*)zone
{
ElementDLLG *element = [super copyWithZone:zone];
[element setValue:value];
return element;
}
- (void)readDataFrom:(TemplateStream *)stream
{
[stream readAmount:sizeof(value) toBuffer:&value];
}
- (unsigned int)sizeOnDisk
{
return sizeof(value);
}
- (void)writeDataTo:(TemplateStream *)stream
{
[stream writeAmount:sizeof(value) fromBuffer:&value];
}
- (void)setValue:(SInt64)v
{
value = v;
}
- (SInt64)value
{
return value;
}
- (NSString *)stringValue
{
return [NSString stringWithFormat:@"%lld", value];
}
- (void)setStringValue:(NSString *)str
{
char cstr[256];
char *endPtr = cstr + 255;
strncpy(cstr, [str cString], 255);
value = strtoll(cstr, &endPtr, 10);
}
@end

View File

@ -0,0 +1,17 @@
#import "Element.h"
@interface ElementDLNG : Element
{
SInt32 value;
}
- (void)setValue:(SInt32)v;
- (SInt32)value;
- (NSString *)stringValue;
- (void)setStringValue:(NSString *)str;
@end
@interface ElementKLNG : ElementDLNG
@end

View File

@ -0,0 +1,53 @@
#import "ElementDLNG.h"
@implementation ElementDLNG
- (id)copyWithZone:(NSZone *)zone
{
ElementDLNG *element = [super copyWithZone:zone];
[element setValue:value];
return element;
}
- (void)readDataFrom:(TemplateStream *)stream
{
[stream readAmount:sizeof(value) toBuffer:&value];
}
- (unsigned int)sizeOnDisk
{
return sizeof(value);
}
- (void)writeDataTo:(TemplateStream *)stream
{
[stream writeAmount:sizeof(value) fromBuffer:&value];
}
- (void)setValue:(SInt32)v
{
value = v;
}
- (SInt32)value
{
return value;
}
- (NSString *)stringValue
{
return [NSString stringWithFormat:@"%ld", value];
}
- (void)setStringValue:(NSString *)str
{
char cstr[256];
char *endPtr = cstr + 255;
strncpy(cstr, [str cString], 255);
value = strtol(cstr, &endPtr, 10);
}
@end
@implementation ElementKLNG
@end

View File

@ -0,0 +1,17 @@
#import "Element.h"
@interface ElementDWRD : Element
{
SInt16 value;
}
- (void)setValue:(SInt16)v;
- (SInt16)value;
- (NSString *)stringValue;
- (void)setStringValue:(NSString *)str;
@end
@interface ElementKWRD : ElementDWRD
@end

View File

@ -0,0 +1,53 @@
#import "ElementDWRD.h"
@implementation ElementDWRD
- (id)copyWithZone:(NSZone *)zone
{
ElementDWRD *element = [super copyWithZone:zone];
[element setValue:value];
return element;
}
- (void)readDataFrom:(TemplateStream *)stream
{
[stream readAmount:sizeof(value) toBuffer:&value];
}
- (unsigned int)sizeOnDisk
{
return sizeof(value);
}
- (void)writeDataTo:(TemplateStream *)stream
{
[stream writeAmount:sizeof(value) fromBuffer:&value];
}
- (void)setValue:(SInt16)v
{
value = v;
}
- (SInt16)value
{
return value;
}
- (NSString *)stringValue
{
return [NSString stringWithFormat:@"%hd", value];
}
- (void)setStringValue:(NSString *)str
{
char cstr[256];
char *endPtr = cstr + 255;
strncpy(cstr, [str cString], 255);
value = strtol(cstr, &endPtr, 10);
}
@end
@implementation ElementKWRD
@end

View File

@ -0,0 +1,14 @@
#import "Element.h"
@interface ElementFBYT : Element
{
unsigned long length;
}
- (void)setLength:(unsigned long)l;
- (unsigned long)length;
- (NSString *)stringValue;
- (void)setStringValue:(NSString *)str;
@end

View File

@ -0,0 +1,70 @@
#import "ElementFBYT.h"
// implements FBYT, FWRD, FLNG, FLLG
@implementation ElementFBYT
- (id)initForType:(NSString *)t withLabel:(NSString *)l
{
self = [super initForType:t withLabel:l];
if(!self) return nil;
if ([t isEqualToString:@"FBYT"]) length = 1;
else if([t isEqualToString:@"FWRD"]) length = 2;
else if([t isEqualToString:@"FLNG"]) length = 4;
else if([t isEqualToString:@"FLLG"]) length = 8;
else length = 0;
return self;
}
- (id)copyWithZone:(NSZone *)zone
{
ElementFBYT *element = [super copyWithZone:zone];
[element setLength:length];
return element;
}
- (void)readDataFrom:(TemplateStream *)stream
{
[stream advanceAmount:length pad:NO];
}
- (unsigned int)sizeOnDisk
{
return length;
}
- (void)writeDataTo:(TemplateStream *)stream
{
[stream advanceAmount:length pad:YES];
}
- (void)setLength:(unsigned long)l
{
length = l;
}
- (unsigned long)length
{
return length;
}
- (NSString *)stringValue
{
return @"";
}
- (void)setStringValue:(NSString *)str
{
}
- (NSString *)label
{
if(length) return @"";
return [super label];
}
- (BOOL)editable
{
return NO;
}
@end

View File

@ -0,0 +1,14 @@
#import "Element.h"
@interface ElementFIXD : Element
{
Fixed value;
}
- (void)setValue:(Fixed)v;
- (Fixed)value;
- (NSString *)stringValue;
- (void)setStringValue:(NSString *)str;
@end

View File

@ -0,0 +1,50 @@
#import "ElementFIXD.h"
@implementation ElementFIXD
- (id)copyWithZone:(NSZone *)zone
{
ElementFIXD *element = [super copyWithZone:zone];
[element setValue:value];
return element;
}
- (void)readDataFrom:(TemplateStream *)stream
{
[stream readAmount:sizeof(value) toBuffer:&value];
}
- (unsigned int)sizeOnDisk
{
return sizeof(value);
}
- (void)writeDataTo:(TemplateStream *)stream
{
[stream writeAmount:sizeof(value) fromBuffer:&value];
}
- (void)setValue:(Fixed)v
{
value = v;
}
- (Fixed)value
{
return value;
}
- (NSString *)stringValue
{
return [NSString stringWithFormat:@"%.3lf", FixedToFloat(value)];
}
- (void)setStringValue:(NSString *)str
{
char cstr[256];
char *endPtr = cstr + 255;
strncpy(cstr, [str cString], 255);
value = FloatToFixed(strtof(cstr, &endPtr));
}
@end

View File

@ -0,0 +1,14 @@
#import "Element.h"
@interface ElementFRAC : Element
{
Fract value;
}
- (void)setValue:(Fract)v;
- (Fract)value;
- (NSString *)stringValue;
- (void)setStringValue:(NSString *)str;
@end

View File

@ -0,0 +1,50 @@
#import "ElementFRAC.h"
@implementation ElementFRAC
- (id)copyWithZone:(NSZone *)zone
{
ElementFRAC *element = [super copyWithZone:zone];
[element setValue:value];
return element;
}
- (void)readDataFrom:(TemplateStream *)stream
{
[stream readAmount:sizeof(value) toBuffer:&value];
}
- (unsigned int)sizeOnDisk
{
return sizeof(value);
}
- (void)writeDataTo:(TemplateStream *)stream
{
[stream writeAmount:sizeof(value) fromBuffer:&value];
}
- (void)setValue:(Fract)v
{
value = v;
}
- (Fract)value
{
return value;
}
- (NSString *)stringValue
{
return [NSString stringWithFormat:@"%.10lg", FractToFloat(value)];
}
- (void)setStringValue:(NSString *)str
{
char cstr[256];
char *endPtr = cstr + 255;
strncpy(cstr, [str cString], 255);
value = FloatToFract(strtof(cstr, &endPtr));
}
@end

View File

@ -0,0 +1,13 @@
#import "Element.h"
@interface ElementHEXD : Element
{
NSData *value;
}
- (void)setValue:(NSData *)d;
- (NSData *)value;
- (NSString *)stringValue;
- (void)setStringValue:(NSString *)str;
@end

View File

@ -0,0 +1,64 @@
#import "ElementHEXD.h"
@implementation ElementHEXD
- (id)copyWithZone:(NSZone *)zone
{
ElementHEXD *element = [super copyWithZone:zone];
[element setValue:value];
return element;
}
- (void)readSubElementsFrom:(TemplateStream *)stream
{
// override to tell stream to stop reading any more TMPL fields
if([stream bytesToGo] > 0)
{
NSLog(@"Warning: Template has fields following hex dump, ignoring them.");
[stream setBytesToGo:0];
}
}
- (void)readDataFrom:(TemplateStream *)stream
{
[self setValue:[NSData dataWithBytes:[stream data] length:[stream bytesToGo]]];
[stream setBytesToGo:0];
}
- (unsigned int)sizeOnDisk
{
return [value length];
}
- (void)writeDataTo:(TemplateStream *)stream
{
[stream writeAmount:[value length] fromBuffer:[value bytes]];
}
- (void)setValue:(NSData *)d
{
id old = value;
value = [d retain];
[old release];
}
- (NSData *)value
{
return value;
}
- (NSString *)stringValue
{
return [value description];
}
- (void)setStringValue:(NSString *)str
{
}
- (BOOL)editable
{
return NO;
}
@end

View File

@ -0,0 +1,15 @@
#import <Cocoa/Cocoa.h>
#import "Element.h"
@interface ElementKEYB : Element
{
NSMutableArray *subElements;
}
- (void)setSubElements:(NSMutableArray *)a;
- (NSMutableArray *)subElements;
@end
@interface ElementKEYE : Element
@end

View File

@ -0,0 +1,105 @@
#import "ElementKEYB.h"
@implementation ElementKEYB
- (id)initForType:(NSString *)t withLabel:(NSString *)l
{
self = [super initForType:t withLabel:l];
if(!self) return nil;
subElements = [[NSMutableArray alloc] init];
return self;
}
- (void)dealloc
{
[subElements release];
[super dealloc];
}
- (id)copyWithZone:(NSZone *)zone
{
return nil;
}
- (void)readSubElementsFrom:(TemplateStream *)stream
{
while([stream bytesToGo] > 0)
{
Element *element = [stream readOneElement];
if([[element type] isEqualToString:@"KEYE"])
break;
[subElements addObject:element];
}
}
- (void)readDataFrom:(TemplateStream *)stream
{
if([[self label] isEqualToString: [[stream key] stringValue]])
{
for(unsigned i = 0; i < [subElements count]; i++)
[[subElements objectAtIndex:i] readDataFrom:stream];
}
}
// Before writeDataTo:is called, this is called to calculate the final resource size:
// This returns the sizes of all our sub-elements. If you subclass, add to that the size
// of this element itself.
- (unsigned int)sizeOnDisk
{
// if(![[self label] isEqualToString: [[stream key] stringValue]])
// return 0;
unsigned int size = 0;
NSEnumerator *enumerator = [subElements objectEnumerator];
while(Element *element = [enumerator nextObject])
size += [element sizeOnDisk];
return size;
}
- (void)writeDataTo:(TemplateStream *)stream
{
if([[self label] isEqualToString: [[stream key] stringValue]])
{
// writes out the data of all our sub-elements here:
NSEnumerator *enumerator = [subElements objectEnumerator];
while(Element *element = [enumerator nextObject])
[element writeDataTo:stream];
}
}
- (void)setSubElements:(NSMutableArray *)a
{
id old = subElements;
subElements = [a retain];
[old release];
}
- (NSMutableArray *)subElements
{
return subElements;
}
- (int)subElementCount
{
return [subElements count];
}
- (Element *)subElementAtIndex:(int)n
{
return [subElements objectAtIndex:n];
}
- (NSString *)stringValue { return @""; }
- (void)setStringValue:(NSString *)str {}
- (BOOL)editable { return NO; }
@end
#pragma mark -
@implementation ElementKEYE
- (void)readDataFrom:(TemplateStream *)stream {}
- (void)writeDataTo:(TemplateStream *)stream {}
- (NSString *)stringValue { return @""; }
- (void)setStringValue:(NSString *)str {}
- (BOOL)editable { return NO; }
@end

View File

@ -0,0 +1,26 @@
#import <Cocoa/Cocoa.h>
#import "Element.h"
@class ElementLSTE;
@class ElementOCNT;
@interface ElementLSTB : Element
{
NSMutableArray *subElements;
ElementLSTB *groupElementTemplate; // TMPL equivalent of self, for cloning
ElementOCNT *countElement; // Our "list counter" element.
}
- (void)readDataForElements:(TemplateStream *)stream;
- (IBAction)createListEntry:(id)sender;
- (void)setSubElements:(NSMutableArray *)a;
- (NSMutableArray *)subElements;
- (void)setGroupElementTemplate:(ElementLSTB *)e;
- (ElementLSTB *)groupElementTemplate;
- (void)setCountElement:(ElementOCNT *)e;
- (ElementOCNT *)countElement;
@end

View File

@ -0,0 +1,249 @@
#import "ElementLSTB.h"
#import "ElementLSTC.h"
#import "ElementLSTE.h"
#import "ElementOCNT.h"
// implements LSTB, LSTZ
@implementation ElementLSTB
- (id)initForType:(NSString *)t withLabel:(NSString *)l
{
self = [super initForType:t withLabel:l];
if(!self) return nil;
subElements = [[NSMutableArray alloc] init];
groupElementTemplate = self;
return self;
}
- (void)dealloc
{
[subElements release];
[super dealloc];
}
- (id)copyWithZone:(NSZone *)zone
{
ElementLSTB *element = [super copyWithZone:zone];
if(!element) return nil;
ElementOCNT *counter = nil;
NSMutableArray *array = [element subElements]; // get empty array created by -initWithType:label:
[element setGroupElementTemplate:groupElementTemplate]; // override group template (init sets this to self)
unsigned count = [[groupElementTemplate subElements] count];
for(unsigned i = 0; i < count; i++)
{
Element *subToClone = [[groupElementTemplate subElements] objectAtIndex:i];
if([subToClone class] == [ElementLSTB class] ||
[subToClone class] == [ElementLSTC class])
{
// instead create a terminating 'LSTE' item, using LSTB/C item's label
ElementLSTE *end = [ElementLSTE elementForType:@"LSTE" withLabel:[subToClone label]];
[array addObject:end];
[end setParentArray:array];
[end setGroupElementTemplate:[(id)subToClone groupElementTemplate]];
[end setCountElement:counter];
if([[subToClone type] isEqualToString:@"LSTZ"])
[end setWritesZeroByte:YES];
}
else
{
Element *clone = [[subToClone copy] autorelease];
if([clone class] == [ElementOCNT class])
counter = (ElementOCNT *)clone;
[array addObject:clone];
}
}
return element;
}
- (void)readSubElementsFrom:(TemplateStream *)stream
{
while([stream bytesToGo] > 0)
{
Element *element = [stream readOneElement];
if([[element type] isEqualToString:@"LSTE"])
{
if([type isEqualToString:@"LSTZ"])
[(ElementLSTE *)element setWritesZeroByte:YES];
break;
}
[subElements addObject:element];
}
}
- (void)readDataForElements:(TemplateStream *)stream
{
int counterValue = 0;
ElementOCNT *counter = nil;
for(unsigned i = 0; i < [subElements count]; i++)
{
Element *element = [subElements objectAtIndex:i];
// set up the counter tracking
if([element class] == [ElementOCNT class]) counter = element;
// decrement the counter if we have list items
if([element class] == [ElementLSTC class]) counterValue--;
// if we get to the end of the list and need more items, create them
if([element class] == [ElementLSTE class] && counterValue > 0)
{
int index = [subElements indexOfObject:element];
while(counterValue--)
{
// create subarray for new data
ElementLSTB *list = [[[(ElementLSTE *)element groupElementTemplate] copy] autorelease];
[subElements insertObject:list atIndex:index++];
[list setParentArray:subElements];
[list setCountElement:counter];
// read data into it and increment loop
[list readDataForElements:stream]; i++;
}
}
// actually read the data for this item
[element readDataFrom:stream];
// now that we've read the (possibly counter) data, save it to the local variable
if(counter) counterValue = [counter value];
}
}
- (void)readDataFrom:(TemplateStream *)stream
{
BOOL isZeroTerminated = [type isEqualToString:@"LSTZ"];
unsigned int bytesToGoAtStart = [stream bytesToGo];
if(isZeroTerminated)
{
char termByte = 0;
[stream peekAmount:1 toBuffer:&termByte];
if(termByte)
[self readDataForElements:stream];
}
else [self readDataForElements:stream];
/* Read additional elements until we have enough items,
except if we're not the first item in our list. */
if(parentArray)
{
while([stream bytesToGo] > 0)
{
if(isZeroTerminated)
{
char termByte = 0;
[stream peekAmount:1 toBuffer:&termByte];
if(termByte == 0) break;
}
// actually read the item
Element *nextItem = [[groupElementTemplate copy] autorelease];
[nextItem setParentArray:nil]; // Make sure it doesn't get into this "if" clause.
[parentArray addObject:nextItem]; // Add it below ourselves.
[nextItem readDataFrom:stream]; // Read it the same way we were.
[nextItem setParentArray:parentArray]; // Set parentArray *after* -readDataFrom: so it doesn't pass the if(parentArray) check above.
}
// now add a terminating 'LSTE' item, using this item's label
ElementLSTE *end = [ElementLSTE elementForType:@"LSTE" withLabel:label];
[parentArray addObject:end];
[end setParentArray:parentArray];
[end setGroupElementTemplate:groupElementTemplate];
[end setCountElement:countElement];
if(isZeroTerminated)
{
[end setWritesZeroByte:YES];
[end readDataFrom:stream];
}
// if it's an empty list delete this LSTB so we only have the empty LSTE.
if(bytesToGoAtStart == 0)
[parentArray removeObject:self];
}
}
// Before writeDataTo:is called, this is called to calculate the final resource size:
// This returns the sizes of all our sub-elements. If you subclass, add to that the size
// of this element itself.
- (unsigned int)sizeOnDisk
{
unsigned int size = 0;
NSEnumerator *enumerator = [subElements objectEnumerator];
while(Element *element = [enumerator nextObject])
size += [element sizeOnDisk];
return size;
}
- (void)writeDataTo:(TemplateStream *)stream
{
// Writes out the data of all our sub-elements here:
NSEnumerator *enumerator = [subElements objectEnumerator];
while(Element *element = [enumerator nextObject])
[element writeDataTo:stream];
}
#pragma mark -
- (void)setSubElements:(NSMutableArray *)a
{
id old = subElements;
subElements = [a retain];
[old release];
}
- (NSMutableArray *)subElements
{
return subElements;
}
- (int)subElementCount
{
return [subElements count];
}
- (Element *)subElementAtIndex:(int)n
{
return [subElements objectAtIndex:n];
}
- (void)setGroupElementTemplate:(ElementLSTB *)e
{
// do not retain, -init sets this to 'self' initially!
groupElementTemplate = e;
}
- (ElementLSTB *)groupElementTemplate
{
return groupElementTemplate;
}
- (void)setCountElement:(ElementOCNT *)e
{
// do not retain sibling element
countElement = e;
}
- (ElementOCNT *)countElement
{
return countElement;
}
- (IBAction)createListEntry:(id)sender
{
ElementLSTB *list = [[groupElementTemplate copy] autorelease];
[parentArray insertObject:list atIndex:[parentArray indexOfObject:self]];
[list setParentArray:parentArray];
[list setCountElement:countElement];
[countElement increment];
}
- (IBAction)clear:(id)sender
{
[countElement decrement];
[parentArray removeObject:self];
}
- (NSString *)stringValue { return @""; }
- (void)setStringValue:(NSString *)str {}
- (BOOL)editable { return NO; }
@end

View File

@ -0,0 +1,4 @@
#import "ElementLSTB.h"
@interface ElementLSTC : ElementLSTB
@end

View File

@ -0,0 +1,60 @@
#import <Cocoa/Cocoa.h>
#import "ElementLSTC.h"
#import "ElementLSTE.h"
#import "ElementOCNT.h"
@implementation ElementLSTC
- (void)readSubElementsFrom:(TemplateStream *)stream
{
while([stream bytesToGo] > 0)
{
Element *element = [stream readOneElement];
if([[element type] isEqualToString:@"LSTE"])
break;
[subElements addObject:element];
}
}
- (void)readDataFrom:(TemplateStream *)stream
{
[self setCountElement:[stream counter]];
unsigned int itemsToGo = [countElement value];
unsigned int itemsToGoAtStart = itemsToGo;
// Read a first item:
if(itemsToGo > 0)
{
[self readDataForElements:stream];
itemsToGo--;
}
/* Read additional elements until we have enough items,
except if we're not the first item in our list. */
if(parentArray)
{
while(itemsToGo--)
{
// Actually read the item:
Element *nextItem = [[groupElementTemplate copy] autorelease]; // Make another list item just like this one.
[nextItem setParentArray:nil]; // Make sure it doesn't get into this "if" clause.
[parentArray addObject:nextItem]; // Add it below ourselves.
[nextItem readDataFrom:stream]; // Read it the same way we were.
[nextItem setParentArray:parentArray]; // Set parentArray *after* -readDataFrom: so it doesn't pass the if(parentArray) check above.
}
// now add a terminating 'LSTE' item, using this item's label
ElementLSTE *end = [ElementLSTE elementForType:@"LSTE" withLabel:label];
[parentArray addObject:end];
[end setParentArray:parentArray];
[end setGroupElementTemplate:groupElementTemplate];
[end setCountElement:countElement];
[stream popCounter];
// if it's an empty list delete this LSTC so we only have the empty LSTE.
if(itemsToGoAtStart == 0)
[parentArray removeObject:self];
}
}
@end

View File

@ -0,0 +1,24 @@
#import <Cocoa/Cocoa.h>
#import "Element.h"
@class ElementOCNT;
@class ElementLSTB;
@interface ElementLSTE : Element
{
ElementLSTB *groupElementTemplate; // The item of which we're to create a copy.
ElementOCNT *countElement; // The "counter" element if we're the end of an LSTC list.
BOOL writesZeroByte; // Write a terminating zero-byte when writing out this item (used by LSTZ).
}
- (IBAction)createListEntry:(id)sender;
- (void)setWritesZeroByte:(BOOL)n;
- (BOOL)writesZeroByte;
- (void)setGroupElementTemplate:(ElementLSTB *)e;
- (ElementLSTB *)groupElementTemplate;
- (void)setCountElement:(ElementOCNT *)e;
- (ElementOCNT *)countElement;
@end

View File

@ -0,0 +1,99 @@
#import "ElementLSTE.h"
#import "ElementLSTB.h"
#import "ElementOCNT.h"
@implementation ElementLSTE
- (id)copyWithZone:(NSZone *)zone
{
ElementLSTE *element = [super copyWithZone:zone];
[element setGroupElementTemplate:groupElementTemplate];
[element setWritesZeroByte:writesZeroByte];
[element setCountElement:countElement];
return element;
}
- (void)dealloc
{
[groupElementTemplate release];
[super dealloc];
}
- (void)setGroupElementTemplate:(ElementLSTB *)e
{
id old = groupElementTemplate;
groupElementTemplate = [e retain];
[old release];
}
- (ElementLSTB *)groupElementTemplate
{
return groupElementTemplate;
}
- (void)setCountElement:(ElementOCNT *)e
{
// do not retain sibling element
countElement = e;
}
- (ElementOCNT *)countElement
{
return countElement;
}
- (void)readSubElementsFrom:(TemplateStream *)stream
{
}
- (void)readDataFrom:(TemplateStream *)stream
{
if(writesZeroByte)
[stream advanceAmount:1 pad:NO];
}
- (unsigned int)sizeOnDisk
{
return writesZeroByte? 1:0;
}
- (void)writeDataTo:(TemplateStream *)stream
{
if(writesZeroByte)
[stream advanceAmount:1 pad:YES];
}
- (void)setWritesZeroByte:(BOOL)n
{
writesZeroByte = n;
}
- (BOOL)writesZeroByte
{
return writesZeroByte;
}
- (IBAction)createListEntry:(id)sender
{
ElementLSTB *list = [[groupElementTemplate copy] autorelease];
[parentArray insertObject:list atIndex:[parentArray indexOfObject:self]];
[list setParentArray:parentArray];
[list setCountElement:countElement];
[countElement increment];
}
- (NSString *)stringValue
{
return @"";
}
- (void)setStringValue:(NSString *)str
{
}
- (BOOL)editable
{
return NO;
}
@end

View File

@ -0,0 +1,19 @@
#import "Element.h"
@interface ElementOCNT : Element
{
unsigned long value;
}
- (BOOL)countFromZero;
- (void)setValue:(unsigned long)v;
- (unsigned long)value;
- (void)increment;
- (void)decrement;
- (NSString *)stringValue;
- (void)setStringValue:(NSString *)str;
@end

View File

@ -0,0 +1,85 @@
#import "ElementOCNT.h"
// implements ZCNT, OCNT, BCNT, BZCT, WCNT, WZCT, LCNT, LZCT
@implementation ElementOCNT
- (id)copyWithZone:(NSZone *)zone
{
ElementOCNT *element = [super copyWithZone:zone];
if(!element) return nil;
// always reset counter on copy
[element setValue:0];
return element;
}
- (void)readDataFrom:(TemplateStream *)stream
{
value = 0;
if ([type isEqualToString:@"LCNT"] || [type isEqualToString:@"LZCT"]) [stream readAmount:4 toBuffer:&value];
else if([type isEqualToString:@"BCNT"] || [type isEqualToString:@"BZCT"]) [stream readAmount:1 toBuffer:(char *)(&value)+3];
else [stream readAmount:2 toBuffer:(short *)(&value)+1];
if([self countFromZero]) value += 1;
}
- (unsigned int)sizeOnDisk
{
if ([type isEqualToString:@"LCNT"] || [type isEqualToString:@"LZCT"]) return 4;
else if([type isEqualToString:@"BCNT"] || [type isEqualToString:@"BZCT"]) return 1;
else return 2;
}
- (void)writeDataTo:(TemplateStream *)stream
{
if([self countFromZero]) value -= 1;
if ([type isEqualToString:@"LCNT"] || [type isEqualToString:@"LZCT"]) [stream writeAmount:4 fromBuffer:&value];
else if([type isEqualToString:@"BCNT"] || [type isEqualToString:@"BZCT"]) [stream writeAmount:1 fromBuffer:(char *)(&value)+3];
else [stream writeAmount:2 fromBuffer:(short *)(&value)+1];
if([self countFromZero]) value += 1;
}
- (BOOL)countFromZero
{
return [type isEqualToString:@"ZCNT"] ||
[type isEqualToString:@"BZCT"] ||
[type isEqualToString:@"WZCT"] ||
[type isEqualToString:@"LZCT"];
}
- (void)setValue:(unsigned long)v
{
value = v;
}
- (unsigned long)value
{
return value;
}
- (void)increment
{
[self setValue:value+1]; // using -setValue for KVO
}
- (void)decrement
{
if(value > 0)
[self setValue:value-1];
}
- (NSString *)stringValue
{
return [NSString stringWithFormat:@"%ld", value];
}
- (void)setStringValue:(NSString *)str
{
}
- (BOOL)editable
{
return NO;
}
@end

View File

@ -0,0 +1,30 @@
#import "Element.h"
enum StringPadding
{
kNoPadding = 0,
kPadToOddLength,
kPadToEvenLength
};
@interface ElementPSTR : Element
{
NSString *value;
UInt32 _maxLength; // for restricted strings
UInt32 _minLength;
enum StringPadding _pad; // for odd- and even-padded strings
BOOL _terminatingByte; // for C strings
int _lengthBytes; // for Pascal strings
int _alignment; // pads end to align on multiple of this
}
- (NSString *)stringValue;
- (void)setStringValue:(NSString *)str;
- (void)setMaxLength:(UInt32)v;
- (void)setMinLength:(UInt32)v;
- (void)setPad:(enum StringPadding)v;
- (void)setTerminatingByte:(BOOL)v;
- (void)setLengthBytes:(int)v;
- (void)setAlignment:(int)v;
@end

View File

@ -0,0 +1,143 @@
#import "ElementPSTR.h"
// implements PSTR, OSTR, ESTR, BSTR, WSTR, LSTR, CSTR, OCST, ECST, CHAR, TNAM
@implementation ElementPSTR
- (id)initForType:(NSString *)t withLabel:(NSString *)l
{
self = [super initForType:t withLabel:l];
if(!self) return nil;
value = [@"" retain];
if ([t isEqualToString:@"PSTR"] ||
[t isEqualToString:@"BSTR"]) { _lengthBytes = 1; _maxLength = UINT8_MAX; _minLength = 0; _terminatingByte = NO; _pad = kNoPadding; _alignment = 0; }
else if([t isEqualToString:@"WSTR"]) { _lengthBytes = 2; _maxLength = UINT16_MAX; _minLength = 0; _terminatingByte = NO; _pad = kNoPadding; _alignment = 0; }
else if([t isEqualToString:@"LSTR"]) { _lengthBytes = 4; _maxLength = UINT32_MAX; _minLength = 0; _terminatingByte = NO; _pad = kNoPadding; _alignment = 0; }
else if([t isEqualToString:@"OSTR"]) { _lengthBytes = 1; _maxLength = UINT8_MAX-1; _minLength = 0; _terminatingByte = NO; _pad = kPadToOddLength; _alignment = 0; }
else if([t isEqualToString:@"ESTR"]) { _lengthBytes = 1; _maxLength = UINT8_MAX; _minLength = 0; _terminatingByte = NO; _pad = kPadToEvenLength; _alignment = 0; }
else if([t isEqualToString:@"CSTR"]) { _lengthBytes = 0; _maxLength = 0; _minLength = 0; _terminatingByte = YES; _pad = kNoPadding; _alignment = 0; }
else if([t isEqualToString:@"OCST"]) { _lengthBytes = 0; _maxLength = 0; _minLength = 0; _terminatingByte = YES; _pad = kPadToOddLength; _alignment = 0; }
else if([t isEqualToString:@"ECST"]) { _lengthBytes = 0; _maxLength = 0; _minLength = 0; _terminatingByte = YES; _pad = kPadToEvenLength; _alignment = 0; }
else if([t isEqualToString:@"CHAR"]) { _lengthBytes = 0; _maxLength = 1; _minLength = 1; _terminatingByte = NO; _pad = kNoPadding; _alignment = 0; }
else if([t isEqualToString:@"TNAM"]) { _lengthBytes = 0; _maxLength = 4; _minLength = 4; _terminatingByte = NO; _pad = kNoPadding; _alignment = 0; }
// temp until keyed values are implemented
else if([t isEqualToString:@"KCHR"]) { _lengthBytes = 0; _maxLength = 1; _minLength = 1; _terminatingByte = NO; _pad = kNoPadding; _alignment = 0; }
else if([t isEqualToString:@"KTYP"]) { _lengthBytes = 0; _maxLength = 4; _minLength = 4; _terminatingByte = NO; _pad = kNoPadding; _alignment = 0; }
return self;
}
- (void)dealloc
{
[value release];
[super dealloc];
}
- (id)copyWithZone:(NSZone*)zone
{
ElementPSTR *element = [super copyWithZone:zone];
[element setStringValue:value];
[element setMaxLength:_maxLength];
[element setMinLength:_minLength];
[element setPad:_pad];
[element setTerminatingByte:_terminatingByte];
[element setLengthBytes:_lengthBytes];
[element setAlignment:_alignment];
return element;
}
- (void)readDataFrom:(TemplateStream *)stream
{
// get string length
UInt32 length = 0;
if(_lengthBytes > 0)
{
[stream readAmount:_lengthBytes toBuffer:&length];
length >>= (4 - _lengthBytes) << 3;
}
if(_terminatingByte)
length += [stream bytesToNull];
if(_maxLength && length > _maxLength) length = _maxLength;
if(length < _minLength) length = _minLength;
// read string
void *buffer = malloc(length);
if(_minLength) memset(buffer, 0, _minLength);
[stream readAmount:length toBuffer:buffer];
if([NSString instancesRespondToSelector:@selector(initWithBytesNoCopy:length:encoding:freeWhenDone:)]) // 10.3
[self setStringValue:[[[NSString alloc] initWithBytesNoCopy:buffer length:length encoding:NSMacOSRomanStringEncoding freeWhenDone:YES] autorelease]];
else
{
[self setStringValue:[[[NSString alloc] initWithBytes:buffer length:length encoding:NSMacOSRomanStringEncoding] autorelease]];
free(buffer);
}
// skip over empty bytes
if(_terminatingByte) [stream advanceAmount:1 pad:NO];
if(_pad == kPadToOddLength && (length + _lengthBytes) % 2 == 0) [stream advanceAmount:1 pad:NO];
if(_pad == kPadToEvenLength && (length + _lengthBytes) % 2 == 1) [stream advanceAmount:1 pad:NO];
// alignment unhandled here
}
- (unsigned int)sizeOnDisk
{
UInt32 length;
if([value respondsToSelector:@selector(lengthOfBytesUsingEncoding:)]) // 10.4
length = [value lengthOfBytesUsingEncoding:NSMacOSRomanStringEncoding];
else length = [value cStringLength];
if(_maxLength && length > _maxLength) length = _maxLength;
if(length < _minLength) length = _minLength;
length += _lengthBytes + (_terminatingByte? 1:0);
if(_pad == kPadToOddLength && length % 2 == 0) length++;
if(_pad == kPadToEvenLength && length % 2 == 1) length++;
// don't know how to deal with alignment here
return length;
}
- (void)writeDataTo:(TemplateStream *)stream
{
// write string
UInt32 length = [value length], writeLength;
if(_maxLength && length > _maxLength) length = _maxLength;
writeLength = length << ((4 - _lengthBytes) << 3);
if(_lengthBytes)
[stream writeAmount:_lengthBytes fromBuffer:&writeLength];
if([value respondsToSelector:@selector(cStringUsingEncoding:)])
[stream writeAmount:length fromBuffer:[value cStringUsingEncoding:NSMacOSRomanStringEncoding]];
else [stream writeAmount:length fromBuffer:[value cString]];
// pad to minimum length with spaces
if(length < _minLength)
{
SInt32 padAmount = _minLength - length;
while(padAmount > 0)
{
UInt32 spaces = ' ';
[stream writeAmount:(padAmount < 4)? padAmount:4 fromBuffer:&spaces];
length += (padAmount < 4)? padAmount:4;
padAmount -= 4;
}
}
if(_terminatingByte) [stream advanceAmount:1 pad:YES];
if(_pad == kPadToOddLength && (length + _lengthBytes + (_terminatingByte? 1:0)) % 2 == 0) [stream advanceAmount:1 pad:YES];
if(_pad == kPadToEvenLength && (length + _lengthBytes + (_terminatingByte? 1:0)) % 2 == 1) [stream advanceAmount:1 pad:YES];
}
- (NSString *)stringValue
{
return value;
}
- (void)setStringValue:(NSString *)str
{
id old = value;
value = [str copy];
[old release];
}
- (void)setMaxLength:(UInt32)v { _maxLength = v; }
- (void)setMinLength:(UInt32)v { _minLength = v; }
- (void)setPad:(enum StringPadding)v { _pad = v; }
- (void)setTerminatingByte:(BOOL)v { _terminatingByte = v; }
- (void)setLengthBytes:(int)v { _lengthBytes = v; }
- (void)setAlignment:(int)v { _alignment = v; }
@end

View File

@ -0,0 +1,14 @@
#import "Element.h"
@interface ElementUBYT : Element
{
UInt8 value;
}
- (void)setValue:(UInt8)v;
- (UInt8)value;
- (NSString *)stringValue;
- (void)setStringValue:(NSString *)str;
@end

View File

@ -0,0 +1,50 @@
#import "ElementUBYT.h"
@implementation ElementUBYT
- (id)copyWithZone:(NSZone *)zone
{
ElementUBYT *element = [super copyWithZone:zone];
[element setValue:value];
return element;
}
- (void)readDataFrom:(TemplateStream *)stream
{
[stream readAmount:sizeof(value) toBuffer:&value];
}
- (unsigned int)sizeOnDisk
{
return sizeof(value);
}
- (void)writeDataTo:(TemplateStream *)stream
{
[stream writeAmount:sizeof(value) fromBuffer:&value];
}
- (void)setValue:(UInt8)v
{
value = v;
}
- (UInt8)value
{
return value;
}
- (NSString *)stringValue
{
return [NSString stringWithFormat:@"%hhu", value];
}
- (void)setStringValue:(NSString *)str
{
char cstr[256];
char *endPtr = cstr + 255;
strncpy(cstr, [str cString], 255);
value = strtoul(cstr, &endPtr, 10);
}
@end

View File

@ -0,0 +1,14 @@
#import "Element.h"
@interface ElementULLG : Element
{
UInt64 value;
}
- (void)setValue:(UInt64)v;
- (UInt64)value;
- (NSString *)stringValue;
- (void)setStringValue:(NSString *)str;
@end

View File

@ -0,0 +1,50 @@
#import "ElementULLG.h"
@implementation ElementULLG
- (id)copyWithZone:(NSZone*)zone
{
ElementULLG *element = [super copyWithZone:zone];
[element setValue:value];
return element;
}
- (void)readDataFrom:(TemplateStream *)stream
{
[stream readAmount:sizeof(value) toBuffer:&value];
}
- (unsigned int)sizeOnDisk
{
return sizeof(value);
}
- (void)writeDataTo:(TemplateStream *)stream
{
[stream writeAmount:sizeof(value) fromBuffer:&value];
}
- (void)setValue:(UInt64)v
{
value = v;
}
- (UInt64)value
{
return value;
}
- (NSString *)stringValue
{
return [NSString stringWithFormat:@"%llu", value];
}
- (void)setStringValue:(NSString *)str
{
char cstr[256];
char *endPtr = cstr + 255;
strncpy(cstr, [str cString], 255);
value = strtoull(cstr, &endPtr, 10);
}
@end

View File

@ -0,0 +1,14 @@
#import "Element.h"
@interface ElementULNG : Element
{
UInt32 value;
}
- (void)setValue:(UInt32)v;
- (UInt32)value;
- (NSString *)stringValue;
- (void)setStringValue:(NSString *)str;
@end

View File

@ -0,0 +1,50 @@
#import "ElementULNG.h"
@implementation ElementULNG
- (id)copyWithZone:(NSZone *)zone
{
ElementULNG *element = [super copyWithZone:zone];
[element setValue:value];
return element;
}
- (void)readDataFrom:(TemplateStream *)stream
{
[stream readAmount:sizeof(value) toBuffer:&value];
}
- (unsigned int)sizeOnDisk
{
return sizeof(value);
}
- (void)writeDataTo:(TemplateStream *)stream
{
[stream writeAmount:sizeof(value) fromBuffer:&value];
}
- (void)setValue:(UInt32)v
{
value = v;
}
- (UInt32)value
{
return value;
}
- (NSString *)stringValue
{
return [NSString stringWithFormat:@"%lu", value];
}
- (void)setStringValue:(NSString *)str
{
char cstr[256];
char *endPtr = cstr + 255;
strncpy(cstr, [str cString], 255);
value = strtoul(cstr, &endPtr, 10);
}
@end

View File

@ -0,0 +1,14 @@
#import "Element.h"
@interface ElementUWRD : Element
{
UInt16 value;
}
- (void)setValue:(UInt16)v;
- (UInt16)value;
- (NSString *)stringValue;
- (void)setStringValue:(NSString *)str;
@end

View File

@ -0,0 +1,50 @@
#import "ElementUWRD.h"
@implementation ElementUWRD
- (id)copyWithZone:(NSZone *)zone
{
ElementUWRD *element = [super copyWithZone:zone];
[element setValue:value];
return element;
}
- (void)readDataFrom:(TemplateStream *)stream
{
[stream readAmount:sizeof(value) toBuffer:&value];
}
- (unsigned int)sizeOnDisk
{
return sizeof(value);
}
- (void)writeDataTo:(TemplateStream *)stream
{
[stream writeAmount:sizeof(value) fromBuffer:&value];
}
- (void)setValue:(UInt16)v
{
value = v;
}
- (UInt16)value
{
return value;
}
- (NSString*)stringValue
{
return [NSString stringWithFormat:@"%hu", value];
}
- (void)setStringValue:(NSString *)str
{
char cstr[256];
char *endPtr = cstr + 255;
strncpy(cstr, [str cString], 255);
value = strtoul(cstr, &endPtr, 10);
}
@end

Binary file not shown.

View File

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>Template Editor</string>
<key>CFBundleIdentifier</key>
<string>com.nickshanks.resknife.templateeditor</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleSignature</key>
<string>ResK</string>
<key>CFBundleVersion</key>
<string>0.1 b2</string>
<key>NSPrincipalClass</key>
<string>TemplateWindowController</string>
<key>RKSupportedTypes</key>
<array>
<dict>
<key>IsResKnifeDefaultForType</key>
<string>YES</string>
<key>RKTypeName</key>
<string>Template Editor</string>
<key>RKTypeRole</key>
<string>Editor</string>
</dict>
</array>
</dict>
</plist>

Binary file not shown.

View File

@ -9,11 +9,11 @@ lists, there is a subclass GroupElement from which you can instead subclass to
have some of the work done for you.
When opening a resource, the template editor first creates a TemplateStream for the template
resource and calls readOneObject: on it until it runs out of data, instantiating a hierarchy
resource and calls readOneObject:on it until it runs out of data, instantiating a hierarchy
of Element subclasses for based on the template.
After that, it creates a TemplateStream for the resource and loops over the template object
hierarchy, creating a copy of each item, and then calling readDataFrom:containingArray: on
hierarchy, creating a copy of each item, and then calling readDataFrom:containingArray:on
the copy, which in turn reads data from the TemplateStream and stores it in its instance
variables. For this to work, subclasses of Element *must* implement the NSCopying
protocol.
@ -28,10 +28,10 @@ resource and passed to all template fields (the copies with the resource data, n
pre-parsed template hierarchy), via writeDataTo:, which is where they should write their
data to the stream. Before that, sizeOnDisk is called on each Element to
calculate the new size of the resource before the actual process of writing them out.
This size must include the size of all of their sub-items, and writeDataTo: must call
This size must include the size of all of their sub-items, and writeDataTo:must call
these sub items if they are to be written to disk.
SPECIAL CASE: LISTS
SPECIAL CASE:LISTS
When the editor encounters an LSTB element, the LSTB element is called upon to parse its
"sub-elements" (the items that make up one list item). The LSTB element reads all elements

Binary file not shown.

After

Width:  |  Height:  |  Size: 274 B

BIN
NuTemplateEditor/TMPLs.rsrc Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -0,0 +1,36 @@
#import <Foundation/Foundation.h>
@class Element;
@interface TemplateStream : NSObject
{
char *data;
unsigned int bytesToGo;
NSMutableArray *counterStack;
NSMutableArray *keyStack;
}
+ (id)streamWithBytes:(char *)d length:(unsigned int)l;
+ (id)substreamWithStream:(TemplateStream *)s length:(unsigned int)l;
- (id)initStreamWithBytes:(char *)d length:(unsigned int)l;
- (id)initWithStream:(TemplateStream *)s length:(unsigned int)l;
- (char *)data;
- (unsigned int)bytesToGo;
- (void)setBytesToGo:(unsigned int)b;
- (Element *)counter;
- (void)pushCounter:(Element *)c;
- (void)popCounter;
- (Element *)key;
- (void)pushKey:(Element *)k;
- (void)popKey;
- (Element *)readOneElement; // For parsing of 'TMPL' resource as template.
- (unsigned int)bytesToNull;
- (void)advanceAmount:(unsigned int)l pad:(BOOL)pad; // advance r/w pointer and optionally write padding bytes
- (void)peekAmount:(unsigned int)l toBuffer:(void *)buffer; // read bytes without advancing pointer
- (void)readAmount:(unsigned int)l toBuffer:(void *)buffer; // stream reading
- (void)writeAmount:(unsigned int)l fromBuffer:(const void *)buffer; // stream writing
- (NSMutableDictionary *)fieldRegistry;
@end

View File

@ -0,0 +1,313 @@
#import "TemplateStream.h"
#import "Element.h"
#import "ElementOCNT.h" // for tracking current counter
#import "ElementHEXD.h" // for errors
#import "ElementDBYT.h"
#import "ElementDWRD.h"
#import "ElementDLNG.h"
#import "ElementDLLG.h"
#import "ElementUBYT.h"
#import "ElementUWRD.h"
#import "ElementULNG.h"
#import "ElementULLG.h"
#import "ElementFIXD.h"
#import "ElementFRAC.h"
#import "ElementFBYT.h"
#import "ElementPSTR.h"
//#import "ElementHEXD.h"
#import "ElementDATE.h"
//#import "ElementOCNT.h"
#import "ElementLSTB.h"
#import "ElementLSTC.h"
#import "ElementLSTE.h"
#import "ElementKEYB.h"
@implementation TemplateStream
+ (id)streamWithBytes:(char *)d length:(unsigned int)l
{
return [[[self alloc] autorelease] initStreamWithBytes:d length:l];
}
+ (id)substreamWithStream:(TemplateStream *)s length:(unsigned int)l
{
return [[[self alloc] autorelease] initWithStream:s length:l];
}
- (id)initStreamWithBytes:(char *)d length:(unsigned int)l
{
self = [super init];
if(!self) return nil;
data = d;
bytesToGo = l;
counterStack = [[NSMutableArray alloc] init];
keyStack = [[NSMutableArray alloc] init];
return self;
}
- (id)initWithStream:(TemplateStream *)s length:(unsigned int)l
{
return [self initStreamWithBytes:[s data] length:MIN(l, [s bytesToGo])];
}
- (void)dealloc
{
[counterStack release];
[keyStack release];
[super dealloc];
}
- (char *)data
{
return data;
}
- (unsigned int)bytesToGo
{
return bytesToGo;
}
- (void)setBytesToGo:(unsigned int)b
{
bytesToGo = b;
}
- (unsigned int)bytesToNull
{
unsigned int dist = 0;
while(dist < bytesToGo)
{
if(*(char *)(data+dist) == 0x00)
return dist;
dist++;
}
return bytesToGo;
}
- (Element *)counter
{
return [counterStack lastObject];
}
- (void)pushCounter:(Element *)c
{
[counterStack addObject:c];
}
- (void)popCounter
{
[counterStack removeLastObject];
}
- (Element *)key
{
NSLog(@"Getting last key of stack: %@", keyStack);
return [counterStack lastObject];
}
- (void)pushKey:(Element *)k
{
[keyStack addObject:k];
NSLog(@"Pushed key to stack: %@", keyStack);
}
- (void)popKey
{
NSLog(@"Popping key from stack: %@", keyStack);
[keyStack removeLastObject];
}
#pragma mark -
- (Element *)readOneElement
{
// check where pointer will be AFTER having loaded this element
NSString *type = nil, *label = nil;
if(*data + 5 <= bytesToGo)
{
bytesToGo -= *data + 5;
label = [[[NSString alloc] initWithBytes:data+1 length:*data encoding:NSMacOSRomanStringEncoding] autorelease];
data += *data +1;
type = [[[NSString alloc] initWithBytes:data length:4 encoding:NSMacOSRomanStringEncoding] autorelease];
data += 4;
}
else
{
bytesToGo = 0;
NSLog(@"Corrupt TMPL resource: not enough data. Dumping remaining resource as hex.");
return [ElementHEXD elementForType:@"HEXD" withLabel:NSLocalizedString(@"Error: Hex Dump", nil)];
}
// create element class
Class class = [[self fieldRegistry] objectForKey:type];
if(class)
{
Element *element = (Element *) [class elementForType:type withLabel:label];
[element readSubElementsFrom:self];
return element;
}
else
{
bytesToGo = 0;
NSLog(@"Class not found for template element type '%@'. Dumping remaining resource as hex.", type);
return [ElementHEXD elementForType:@"HEXD" withLabel:NSLocalizedString(@"Error: Hex Dump", nil)];
}
}
- (void)advanceAmount:(unsigned int)l pad:(BOOL)pad
{
if(l > bytesToGo) l = bytesToGo;
if(l > 0)
{
if(pad) memset(data, 0, l);
data += l;
bytesToGo -= l;
}
}
- (void)peekAmount:(unsigned int)l toBuffer:(void *)buffer
{
if(l > bytesToGo) l = bytesToGo;
if(l > 0) memmove(buffer, data, l);
}
- (void)readAmount:(unsigned int)l toBuffer:(void *)buffer
{
if(l > bytesToGo) l = bytesToGo;
if(l > 0)
{
memmove(buffer, data, l);
data += l;
bytesToGo -= l;
}
}
- (void)writeAmount:(unsigned int)l fromBuffer:(const void *)buffer
{
if(l > bytesToGo) l = bytesToGo;
if(l > 0)
{
memmove(data, buffer, l);
data += l;
bytesToGo -= l;
}
}
#pragma mark -
#pragma mark Misc
- (NSMutableDictionary *)fieldRegistry
{
static NSMutableDictionary *registry = nil;
if(!registry)
{
registry = [[NSMutableDictionary alloc] init];
// integers
[registry setObject:[ElementDBYT class] forKey:@"DBYT"]; // signed ints
[registry setObject:[ElementDWRD class] forKey:@"DWRD"];
[registry setObject:[ElementDLNG class] forKey:@"DLNG"];
[registry setObject:[ElementDLLG class] forKey:@"DLLG"];
[registry setObject:[ElementUBYT class] forKey:@"UBYT"]; // unsigned ints
[registry setObject:[ElementUWRD class] forKey:@"UWRD"];
[registry setObject:[ElementULNG class] forKey:@"ULNG"];
[registry setObject:[ElementULLG class] forKey:@"ULLG"];
[registry setObject:[ElementFBYT class] forKey:@"FBYT"]; // filler ints
[registry setObject:[ElementFBYT class] forKey:@"FWRD"];
[registry setObject:[ElementFBYT class] forKey:@"FLNG"];
[registry setObject:[ElementFBYT class] forKey:@"FLLG"];
// fractions
[registry setObject:[ElementFIXD class] forKey:@"FIXD"]; // 16.16 fixed fraction
[registry setObject:[ElementFRAC class] forKey:@"FRAC"]; // 2.30 fixed fraction
// strings
[registry setObject:[ElementPSTR class] forKey:@"PSTR"];
[registry setObject:[ElementPSTR class] forKey:@"BSTR"];
[registry setObject:[ElementPSTR class] forKey:@"WSTR"];
[registry setObject:[ElementPSTR class] forKey:@"LSTR"];
[registry setObject:[ElementPSTR class] forKey:@"OSTR"];
[registry setObject:[ElementPSTR class] forKey:@"ESTR"];
[registry setObject:[ElementPSTR class] forKey:@"CSTR"];
[registry setObject:[ElementPSTR class] forKey:@"OCST"];
[registry setObject:[ElementPSTR class] forKey:@"ECST"];
[registry setObject:[ElementPSTR class] forKey:@"CHAR"];
[registry setObject:[ElementPSTR class] forKey:@"TNAM"];
// hex dumps
[registry setObject:[ElementHEXD class] forKey:@"HEXD"];
// list counters
[registry setObject:[ElementOCNT class] forKey:@"OCNT"];
[registry setObject:[ElementOCNT class] forKey:@"ZCNT"];
[registry setObject:[ElementOCNT class] forKey:@"BCNT"];
[registry setObject:[ElementOCNT class] forKey:@"BZCT"];
[registry setObject:[ElementOCNT class] forKey:@"WCNT"];
[registry setObject:[ElementOCNT class] forKey:@"WZCT"];
[registry setObject:[ElementOCNT class] forKey:@"LCNT"];
[registry setObject:[ElementOCNT class] forKey:@"LZCT"];
// list begin/end
[registry setObject:[ElementLSTC class] forKey:@"LSTC"];
[registry setObject:[ElementLSTB class] forKey:@"LSTB"];
[registry setObject:[ElementLSTB class] forKey:@"LSTZ"];
[registry setObject:[ElementLSTE class] forKey:@"LSTE"];
// key begin/end
[registry setObject:[ElementKEYB class] forKey:@"KEYB"];
[registry setObject:[ElementKEYE class] forKey:@"KEYE"];
// dates
[registry setObject:[ElementDATE class] forKey:@"DATE"]; // 4-byte date (seconds since 1 Jan 1904)
[registry setObject:[ElementDATE class] forKey:@"MDAT"];
// and some faked ones just to increase compatibility (these are marked 'x' in the docs)
[registry setObject:[ElementUBYT class] forKey:@"HBYT"]; // hex byte/word/long
[registry setObject:[ElementUWRD class] forKey:@"HWRD"];
[registry setObject:[ElementULNG class] forKey:@"HLNG"];
[registry setObject:[ElementULLG class] forKey:@"HLLG"];
[registry setObject:[ElementKBYT class] forKey:@"KBYT"]; // signed keys
[registry setObject:[ElementKWRD class] forKey:@"KWRD"];
[registry setObject:[ElementKLNG class] forKey:@"KLNG"];
[registry setObject:[ElementDLLG class] forKey:@"KLLG"];
[registry setObject:[ElementUBYT class] forKey:@"KUBT"]; // unsigned keys
[registry setObject:[ElementUWRD class] forKey:@"KUWD"];
[registry setObject:[ElementULNG class] forKey:@"KULG"];
[registry setObject:[ElementULLG class] forKey:@"KULL"];
[registry setObject:[ElementUBYT class] forKey:@"KHBT"]; // hex keys
[registry setObject:[ElementUWRD class] forKey:@"KHWD"];
[registry setObject:[ElementULNG class] forKey:@"KHLG"];
[registry setObject:[ElementULLG class] forKey:@"KHLL"];
[registry setObject:[ElementPSTR class] forKey:@"KCHR"]; // keyed MacRoman values
[registry setObject:[ElementPSTR class] forKey:@"KTYP"];
[registry setObject:[ElementFBYT class] forKey:@"KRID"]; // key on ID of the resource
[registry setObject:[ElementUWRD class] forKey:@"BOOL"]; // true = 256; false = 0
[registry setObject:[ElementUBYT class] forKey:@"BFLG"]; // binary flag the size of a byte/word/long
[registry setObject:[ElementUWRD class] forKey:@"WFLG"];
[registry setObject:[ElementULNG class] forKey:@"LFLG"];
[registry setObject:[ElementDWRD class] forKey:@"RSID"]; // resouce id (signed word)
[registry setObject:[ElementULNG class] forKey:@"REAL"]; // single precision float
[registry setObject:[ElementULLG class] forKey:@"DOUB"]; // double precision float
[registry setObject:[ElementUWRD class] forKey:@"SFRC"]; // 0.16 fixed fraction
[registry setObject:[ElementUWRD class] forKey:@"FXYZ"]; // 1.15 fixed fraction
[registry setObject:[ElementUWRD class] forKey:@"FWID"]; // 4.12 fixed fraction
[registry setObject:[ElementFBYT class] forKey:@"CASE"];
[registry setObject:[ElementFBYT class] forKey:@"TITL"]; // resource title (e.g. utxt would have "Unicode Text"; must be first element of template, and not anywhere else)
[registry setObject:[ElementFBYT class] forKey:@"CMNT"];
[registry setObject:[ElementFBYT class] forKey:@"DVDR"];
[registry setObject:[ElementULLG class] forKey:@"LLDT"]; // 8-byte date (LongDateTime; seconds since 1 Jan 1904)
[registry setObject:[ElementDBYT class] forKey:@"STYL"]; // QuickDraw font style
[registry setObject:[ElementULNG class] forKey:@"PNT "]; // QuickDraw point
[registry setObject:[ElementULLG class] forKey:@"RECT"]; // QuickDraw rect
[registry setObject:[ElementDWRD class] forKey:@"SCPC"]; // MacOS script code (ScriptCode)
[registry setObject:[ElementDWRD class] forKey:@"LNGC"]; // MacOS language code (LangCode)
[registry setObject:[ElementDWRD class] forKey:@"RGNC"]; // MacOS region code (RegionCode)
// unhandled types at present, see file:///Users/nicholas/Sites/resknife.sf.net/resorcerer_comparison.html
// BBIT, BBnn, FBIT, FBnn, WBIT, WBnn
// Pnnn, Cnnn, Hnnn, Fnnn, HEXD
// AWRD, ALNG (not so easy, element needs to know how much data preceeds it in the stream)
}
return registry;
}
@end

View File

@ -0,0 +1,53 @@
/* =============================================================================
PROJECT: ResKnife
FILE: TemplateWindowController.h
PURPOSE: This is the main class of our template editor. Every
resource editor's main class implements the
ResKnifePluginProtocol. Every editor should implement
initWithResource:. Only implement initWithResources:if you feel
like writing a template editor.
Note that your plugin is responsible for committing suicide
after its window has been closed. If you subclass it from
NSWindowController, the controller will take care of that
for you, according to a guy named Doug.
AUTHORS: M. Uli Kusterer, witness(at)zathras.de, (c) 2003.
REVISIONS:
2003-07-31 UK Created.
========================================================================== */
#import <Cocoa/Cocoa.h>
#import "ResKnifePluginProtocol.h"
#import "ResKnifeResourceProtocol.h"
@interface TemplateWindowController : NSWindowController <ResKnifeTemplatePluginProtocol>
{
IBOutlet NSOutlineView *displayList; // template display (debug only).
IBOutlet NSOutlineView *dataList; // Data display.
IBOutlet NSDrawer *tmplDrawer;
NSMutableDictionary *toolbarItems;
NSMutableArray *templateStructure; // Pre-parsed form of our template.
NSMutableArray *resourceStructure; // Parsed form of our resource.
id <ResKnifeResourceProtocol> resource; // The resource we operate on.
id <ResKnifeResourceProtocol> backup; // The original resource.
BOOL liveEdit;
}
- (void)setupToolbar;
- (void)readTemplate:(id <ResKnifeResourceProtocol>)tmplRes;
- (void)loadResource;
- (IBAction)saveResource:(id)sender;
- (IBAction)revertResource:(id)sender;
- (IBAction)createListEntry:(id)sender;
- (IBAction)cut:(id)sender;
- (IBAction)copy:(id)sender;
- (IBAction)paste:(id)sender;
- (IBAction)clear:(id)sender;
@end
@interface NTOutlineView : NSOutlineView
@end

View File

@ -0,0 +1,461 @@
#import "TemplateWindowController.h"
#import "TemplateStream.h"
#import "Element.h"
#import "ElementOCNT.h"
#import "ElementLSTE.h"
// and ones for keyed fields
#import "ElementDBYT.h"
#import "ElementDWRD.h"
#import "ElementDLNG.h"
#import "NSOutlineView-SelectedItems.h"
@implementation TemplateWindowController
- (id)initWithResource:(id <ResKnifeResourceProtocol>)newResource
{
return [self initWithResources:newResource, nil];
}
- (id)initWithResources:(id <ResKnifeResourceProtocol>)newResource, ...
{
id tmplResource;
va_list resourceList;
va_start(resourceList, newResource);
self = [self initWithWindowNibName:@"TemplateWindow"];
if(!self)
{
va_end(resourceList);
return nil;
}
toolbarItems = [[NSMutableDictionary alloc] init];
// undoManager = [[NSUndoManager alloc] init];
liveEdit = NO;
if(liveEdit)
{
resource = [(id)newResource retain]; // resource to work on
backup = [(NSObject *)resource copy]; // for reverting only
}
else
{
backup = [(id)newResource retain]; // actual resource to change when saving data
resource = [(NSObject *)backup copy]; // resource to work on
}
templateStructure = [[NSMutableArray alloc] init];
resourceStructure = [[NSMutableArray alloc] init];
tmplResource = va_arg(resourceList, id);
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(templateDataDidChange:) name:ResourceDataDidChangeNotification object:tmplResource];
[self readTemplate:tmplResource]; // reads (but doesn't retain) the template for this resource (TMPL resource with name equal to the passed resource's type)
while(tmplResource = va_arg(resourceList, id))
NSLog(@"Too many params passed to -initWithResources:%@", [tmplResource description]);
va_end(resourceList);
// load the window from the nib
[self setShouldCascadeWindows:YES];
[self window];
return self;
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
[toolbarItems release];
[templateStructure release];
[resourceStructure release];
[(id)resource release];
[(id)backup release];
[super dealloc];
}
/*
- (void)windowControllerDidLoadNib:(NSWindowController *)controller
{
[super windowControllerDidLoadNib:controller];
[self setupToolbar:controller];
}
*/
- (void)windowDidLoad
{
[super windowDidLoad];
[self setupToolbar];
[self loadResource];
if(liveEdit) [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(resourceDataDidChange:) name:ResourceDataDidChangeNotification object:resource];
else [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(resourceDataDidChange:) name:ResourceDataDidChangeNotification object:backup];
[[self window] setTitle:[resource defaultWindowTitle]];
[self showWindow:self];
[displayList reloadData];
}
- (void)templateDataDidChange:(NSNotification *)notification
{
[templateStructure removeAllObjects];
[self readTemplate:[notification object]];
if([self isWindowLoaded])
[self loadResource];
}
- (void)resourceDataDidChange:(NSNotification *)notification
{
if(!liveEdit)
// bug: should display alert asking if you want to replace data in this editor or reassert this data, revoking the other editor's changes
[resource setData:[[backup data] copy]];
[self loadResource];
}
- (void)loadResource
{
// create new stream
TemplateStream *stream = [TemplateStream streamWithBytes:(char *)[[resource data] bytes] length:[[resource data] length]];
// loop through template cloning its elements
[resourceStructure removeAllObjects];
NSEnumerator *enumerator = [templateStructure objectEnumerator];
while(Element *element = [enumerator nextObject])
{
Element *clone = [[element copy] autorelease]; // copy the template object.
NSLog(@"clone = %@; resourceStructure = %@", clone, resourceStructure);
[resourceStructure addObject:clone]; // add it to our parsed resource data list. Do this right away so the element can append other items should it desire to.
[clone setParentArray:resourceStructure]; // the parent for these is the root level resourceStructure object
Class cc = [clone class];
BOOL pushedCounter = NO;
BOOL pushedKey = NO;
if(cc == [ElementOCNT class])
{ [stream pushCounter:clone]; pushedCounter = YES; }
if(cc == [ElementKBYT class] ||
cc == [ElementKWRD class] ||
cc == [ElementKLNG class] )
{ [stream pushKey:clone]; pushedKey = YES; }
[clone readDataFrom:stream]; // fill it with resource data.
if(cc == [ElementLSTE class] && pushedCounter)
[stream popCounter];
// if(cc == [ElementKEYE class] && pushedKey)
// [stream popKey];
}
// reload the view
[dataList reloadData];
}
- (BOOL)windowShouldClose:(id)sender
{
[[self window] makeFirstResponder:dataList];
[dataList abortEditing];
if([[self window] isDocumentEdited])
{
NSBeginAlertSheet(@"Do you want to keep the changes you made to this resource?", @"Keep", @"Don't Keep", @"Cancel", sender, self, @selector(saveSheetDidClose:returnCode:contextInfo:), nil, nil, @"Your changes cannot be saved later if you don't keep them.");
return NO;
}
else return YES;
}
- (void)saveSheetDidClose:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
{
switch(returnCode)
{
case NSAlertDefaultReturn: // keep
[self saveResource:nil];
[[self window] close];
break;
case NSAlertAlternateReturn: // don't keep
[[self window] close];
break;
case NSAlertOtherReturn: // cancel
break;
}
}
- (void)saveResource:(id)sender
{
// get size of resource by summing size of all fields
unsigned int size = 0;
NSEnumerator *enumerator = [resourceStructure objectEnumerator];
while(Element *element = [enumerator nextObject])
size += [element sizeOnDisk];
// create data and stream
NSMutableData *newData = [NSMutableData dataWithLength:size];
TemplateStream *stream = [TemplateStream streamWithBytes:(char *)[newData bytes] length:size];
// write bytes into new data object
enumerator = [resourceStructure objectEnumerator];
while(Element *element = [enumerator nextObject])
[element writeDataTo:stream];
// send the new resource data to ResKnife
if(liveEdit)
{
[[NSNotificationCenter defaultCenter] removeObserver:self name:ResourceDataDidChangeNotification object:resource];
[resource setData:newData];
[backup setData:[newData copy]];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(resourceDataDidChange:) name:ResourceDataDidChangeNotification object:resource];
}
else
{
[[NSNotificationCenter defaultCenter] removeObserver:self name:ResourceDataDidChangeNotification object:backup];
[resource setData:newData];
[backup setData:[newData copy]];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(resourceDataDidChange:) name:ResourceDataDidChangeNotification object:backup];
[self setDocumentEdited:NO];
}
}
- (void)revertResource:(id)sender
{
[resource setData:[[backup data] copy]];
}
- (void)readTemplate:(id<ResKnifeResourceProtocol>)tmplRes
{
char *data = (char*) [[tmplRes data] bytes];
unsigned long bytesToGo = [[tmplRes data] length];
TemplateStream *stream = [TemplateStream streamWithBytes:data length:bytesToGo];
// read new fields from the template and add them to our list
while([stream bytesToGo] > 0)
{
Element *element = [stream readOneElement];
if(element)
{
[element setIsTMPL:YES]; // for debugging
[templateStructure addObject:element];
}
else
{
NSLog(@"Error reading template stream, aborting.");
break;
}
}
[displayList reloadData];
}
#pragma mark -
#pragma mark Table Management
- (id)outlineView:(NSOutlineView*)outlineView child:(int)index ofItem:(id)item
{
if((item == nil) && (outlineView == displayList))
return [templateStructure objectAtIndex:index];
else if((item == nil) && (outlineView == dataList))
return [resourceStructure objectAtIndex:index];
else return [item subElementAtIndex:index];
}
- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item
{
return ([item subElementCount] > 0);
}
- (int)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item
{
if((item == nil) && (outlineView == displayList))
return [templateStructure count];
else if((item == nil) && (outlineView == dataList))
return [resourceStructure count];
else return [item subElementCount];
}
- (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
{
return [item valueForKey:[tableColumn identifier]];
}
- (void)outlineView:(NSOutlineView *)outlineView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
{
id old = [item valueForKey:[tableColumn identifier]];
if([(Element *)item editable] && ![old isEqual:object])
{
// [[self undoManager] registerUndoWithTarget:item selector:@selector(setStringValue:) object:old];
// [[self undoManager] setActionName:NSLocalizedString(@"Changes", nil)];
[item setValue:object forKey:[tableColumn identifier]];
if(!liveEdit) [self setDocumentEdited:YES];
// remove self to avoid reloading the resource
[[NSNotificationCenter defaultCenter] removeObserver:self name:ResourceDataDidChangeNotification object:resource];
[[NSNotificationCenter defaultCenter] postNotificationName:ResourceDataDidChangeNotification object:resource];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(resourceDataDidChange:) name:ResourceDataDidChangeNotification object:resource];
}
}
- (BOOL)outlineView:(NSOutlineView *)outlineView shouldEditTableColumn:(NSTableColumn *)tableColumn item:(id)item
{
return [(Element *)item editable];
}
/*- (float)outlineView:(NSOutlineView *)outlineView heightOfRowByItem:(id)item
{
return [item rowHeight];
}*/
#pragma mark -
#pragma mark Menu Management
// these next five methods are a crude hack - the items ought ot be in the responder chain themselves
- (IBAction)createListEntry:(id)sender;
{
// This works by selecting an item that serves as a template (another LSTB), or knows how to create an item (LSTE) and passing the message on to it.
id element = [dataList selectedItem];
if([element respondsToSelector:@selector(createListEntry:)])
{
[element createListEntry:sender];
[dataList reloadData];
[dataList expandItem:[dataList selectedItem] expandChildren:YES];
if(!liveEdit) [self setDocumentEdited:YES];
}
}
- (IBAction)cut:(id)sender;
{
[[dataList selectedItem] cut:sender];
[dataList reloadData];
if(!liveEdit) [self setDocumentEdited:YES];
}
- (IBAction)copy:(id)sender;
{
[[dataList selectedItem] copy:sender];
[dataList reloadData];
}
- (IBAction)paste:(id)sender;
{
[[dataList selectedItem] paste:sender];
[dataList reloadData];
if(!liveEdit) [self setDocumentEdited:YES];
}
- (IBAction)clear:(id)sender;
{
[[dataList selectedItem] clear:sender];
[dataList reloadData];
if(!liveEdit) [self setDocumentEdited:YES];
}
- (BOOL)validateMenuItem:(NSMenuItem*)item
{
Element *element = (Element*) [dataList selectedItem];
if([item action] == @selector(createListEntry:)) return(element && [element respondsToSelector:@selector(createListEntry:)]);
else if([item action] == @selector(cut:)) return(element && [element respondsToSelector:@selector(cut:)]);
else if([item action] == @selector(copy:)) return(element && [element respondsToSelector:@selector(copy:)]);
else if([item action] == @selector(paste:) && element && [element respondsToSelector:@selector(validateMenuItem:)])
return([element validateMenuItem:item]);
else if([item action] == @selector(clear:)) return(element && [element respondsToSelector:@selector(clear:)]);
else if([item action] == @selector(saveDocument:)) return YES;
else return NO;
}
- (void)windowDidBecomeKey:(NSNotification *)notification
{
NSMenu *resourceMenu = [[[NSApp mainMenu] itemAtIndex:3] submenu];
NSMenuItem *createItem = [resourceMenu itemAtIndex:[resourceMenu indexOfItemWithTarget:nil andAction:@selector(showCreateResourceSheet:)]];
[createItem setTitle:NSLocalizedString(@"Create List Entry", nil)];
[createItem setAction:@selector(createListEntry:)];
}
- (void)windowDidResignKey:(NSNotification *)notification
{
NSMenu *resourceMenu = [[[NSApp mainMenu] itemAtIndex:3] submenu];
NSMenuItem *createItem = [resourceMenu itemAtIndex:[resourceMenu indexOfItemWithTarget:nil andAction:@selector(createListEntry:)]];
[createItem setTitle:NSLocalizedString(@"Create New Resource...", nil)];
[createItem setAction:@selector(showCreateResourceSheet:)];
}
#pragma mark -
#pragma mark Toolbar Management
static NSString *RKTEToolbarIdentifier = @"com.nickshanks.resknife.templateeditor.toolbar";
static NSString *RKTEDisplayTMPLIdentifier = @"com.nickshanks.resknife.templateeditor.toolbar.tmpl";
- (void)setupToolbar
{
/* This routine should become invalid once toolbars are integrated into nib files */
NSToolbarItem *item;
[toolbarItems removeAllObjects]; // just in case this method is called more than once per document (which it shouldn't be!)
item = [[[NSToolbarItem alloc] initWithItemIdentifier:RKTEDisplayTMPLIdentifier] autorelease];
[item setLabel:NSLocalizedString(@"Parsed TMPL", nil)];
[item setPaletteLabel:NSLocalizedString(@"Display Parsed TMPL", nil)];
[item setToolTip:NSLocalizedString(@"Display Parsed TMPL", nil)];
[item setImage:[NSImage imageNamed:@"DisplayTMPL"]];
[item setTarget:self];
[item setAction:@selector(displayParsedTMPL:)];
[toolbarItems setObject:item forKey:RKTEDisplayTMPLIdentifier];
NSToolbar *toolbar = [[[NSToolbar alloc] initWithIdentifier:RKTEToolbarIdentifier] autorelease];
// set toolbar properties
[toolbar setVisible:NO];
[toolbar setAutosavesConfiguration:YES];
[toolbar setAllowsUserCustomization:YES];
[toolbar setDisplayMode:NSToolbarDisplayModeLabelOnly];
[toolbar setSizeMode:NSToolbarSizeModeSmall];
// attach toolbar to window
[toolbar setDelegate:self];
[[self window] setToolbar:toolbar];
}
- (NSToolbarItem *)toolbar:(NSToolbar *)toolbar itemForItemIdentifier:(NSString *)itemIdentifier willBeInsertedIntoToolbar:(BOOL)flag
{
return [toolbarItems objectForKey:itemIdentifier];
}
- (NSArray *)toolbarDefaultItemIdentifiers:(NSToolbar *)toolbar
{
return [NSArray arrayWithObjects:RKTEDisplayTMPLIdentifier, NSToolbarFlexibleSpaceItemIdentifier, NSToolbarPrintItemIdentifier, nil];
}
- (NSArray *)toolbarAllowedItemIdentifiers:(NSToolbar *)toolbar
{
return [NSArray arrayWithObjects:RKTEDisplayTMPLIdentifier, NSToolbarPrintItemIdentifier, NSToolbarCustomizeToolbarItemIdentifier, NSToolbarFlexibleSpaceItemIdentifier, NSToolbarSpaceItemIdentifier, NSToolbarSeparatorItemIdentifier, nil];
}
/*
- (BOOL)validateToolbarItem:(NSToolbarItem *)item
{
return YES;
}
*/
- (void)displayParsedTMPL:(id)sender
{
[tmplDrawer toggle:sender];
}
@end
#pragma mark -
#pragma mark Table Event Handling
@implementation NTOutlineView
- (void)keyDown:(NSEvent *)event
{
Element *selectedItem = nil;
int selectedRow = [self selectedRow];
if(selectedRow != -1)
selectedItem = [self selectedItem];
if(selectedItem && [selectedItem editable] && ([[event characters] isEqualToString:@"\r"] || [[event characters] isEqualToString:@"\t"]))
[self editColumn:1 row:selectedRow withEvent:nil select:YES];
else if(selectedItem && [selectedItem respondsToSelector:@selector(clear:)] && [[event characters] isEqualToString:[NSString stringWithCString:"\x7F"]])
[[[self window] windowController] clear:nil];
else [super keyDown:event];
}
- (BOOL)textView:(NSTextView *)textView doCommandBySelector:(SEL)selector
{
// pressed return, end editing
if(selector == @selector(insertNewline:))
{
[[self window] makeFirstResponder:self];
[self abortEditing];
return YES;
}
return [super textView:textView doCommandBySelector:selector];
}
@end

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB