Hex Editor UI working.

This commit is contained in:
Nicholas Shanks 2002-02-02 11:48:54 +00:00
parent 5c6b25570d
commit 85b7550e17
12 changed files with 557 additions and 150 deletions

View File

@ -0,0 +1,17 @@
#import <Cocoa/Cocoa.h>
@interface HexEditorDelegate : NSObject
{
IBOutlet NSTextView *ascii;
IBOutlet NSTextView *hex;
IBOutlet NSTextView *offset;
}
- (NSString *)offsetRepresentation:(NSData *)data;
- (NSString *)hexRepresentation:(NSData *)data;
- (NSString *)asciiRepresentation:(NSData *)data;
- (NSTextView *)hex;
- (NSTextView *)ascii;
@end

View File

@ -0,0 +1,215 @@
#import "HexEditorDelegate.h"
@implementation HexEditorDelegate
/* data re-representation methods */
- (NSString *)offsetRepresentation:(NSData *)data;
{
int row;
int rows = ([data length] / 16) + (([data length] % 16)? 1:0);
NSMutableString *representation = [NSMutableString string];
for( row = 0; row < rows; row++ )
{
[representation appendFormat:@"%08lX:\n", row * 16];
}
// remove last character (the return on the end)
[representation deleteCharactersInRange:NSMakeRange([representation length] -1, 1)];
return representation;
}
- (NSString *)hexRepresentation:(NSData *)data;
{
int row, addr, currentByte = 0, dataLength = [data length];
int rows = (dataLength / 16) + ((dataLength % 16)? 1:0);
char buffer[16*3], byte, hex1, hex2;
NSMutableString *representation = [NSMutableString string];
// draw bytes
for( row = 0; row < rows; row++ )
{
for( addr = 0; addr < 16; addr++ )
{
if( currentByte < dataLength )
{
[data getBytes:&byte range:NSMakeRange(currentByte, 1)];
hex1 = byte;
hex2 = byte;
hex1 >>= 4;
hex1 &= 0x0F;
hex2 &= 0x0F;
hex1 += (hex1 < 10)? 0x30 : 0x37;
hex2 += (hex2 < 10)? 0x30 : 0x37;
buffer[addr*3] = hex1;
buffer[addr*3 +1] = hex2;
buffer[addr*3 +2] = 0x20;
// advance current byte
currentByte++;
}
else
{
buffer[addr*3] = 0x00;
break;
}
}
// clear last byte on line
buffer[16*3 -1] = 0x00;
// append buffer to representation
[representation appendString:[NSString stringWithCString:buffer]];
if( currentByte != dataLength )
[representation appendString:@"\n"];
}
return representation;
}
- (NSString *)asciiRepresentation:(NSData *)data;
{
int row, addr, currentByte = 0, dataLength = [data length];
int rows = (dataLength / 16) + ((dataLength % 16)? 1:0);
char buffer[17], byte = 0x00;
NSMutableString *representation = [NSMutableString string];
// draw bytes
for( row = 0; row < rows; row++ )
{
for( addr = 0; addr < 16; addr++ )
{
if( currentByte < dataLength )
{
[data getBytes:&byte range:NSMakeRange(currentByte, 1)];
if( byte >= 0x20 && byte < 0x7F )
buffer[addr] = byte;
else buffer[addr] = 0x2E; // full stop
// advance current byte
currentByte++;
}
else
{
buffer[addr] = 0x00;
break;
}
}
// clear last byte on line
buffer[16] = 0x00;
// append buffer to representation
[representation appendString:[NSString stringWithCString:buffer]];
if( currentByte != dataLength )
[representation appendString:@"\n"];
}
return representation;
}
/* delegation methods */
- (NSRange)textView:(NSTextView *)textView willChangeSelectionFromCharacterRange:(NSRange)oldSelectedCharRange toCharacterRange:(NSRange)newSelectedCharRange
{
NSRange hexRange, asciiRange;
// temporarilly removing the delegate stops this function being called recursivly!
id oldDelegate = [hex delegate];
[hex setDelegate:nil];
[ascii setDelegate:nil];
if( textView == hex ) // we're selecting hexadecimal
{
if( newSelectedCharRange.length == 0 ) // moving insertion point
{
asciiRange = NSMakeRange( newSelectedCharRange.location /3, 0 );
}
else // dragging a selection
{
int numReturns = (newSelectedCharRange.length /47) + (newSelectedCharRange.location % 47? 1:0);
asciiRange = NSMakeRange( newSelectedCharRange.location /3, (newSelectedCharRange.length+1) /3 );
}
NSLog( @"hex selection changed from %@ to %@", NSStringFromRange(oldSelectedCharRange), NSStringFromRange(newSelectedCharRange) );
NSLog( @"changing ascii selection to %@", NSStringFromRange(asciiRange) );
[ascii setSelectedRange:asciiRange];
}
else if( textView == ascii ) // we're selecting ASCII
{
if( newSelectedCharRange.length == 0 ) // moving insertion point
{
hexRange = NSMakeRange( newSelectedCharRange.location *3, 0 );
}
else // dragging a selection
{
int numReturns = (newSelectedCharRange.length /17) + (newSelectedCharRange.location % 17? 1:0);
hexRange = NSMakeRange( (newSelectedCharRange.location - numReturns) *3 + numReturns, ((newSelectedCharRange.length - numReturns) *3) -1 );
}
NSLog( @"ascii selection changed from %@ to %@", NSStringFromRange(oldSelectedCharRange), NSStringFromRange(newSelectedCharRange) );
NSLog( @"changing hex selection to %@", NSStringFromRange(hexRange) );
[hex setSelectedRange:hexRange];
}
// restore delegates
[hex setDelegate:oldDelegate];
[ascii setDelegate:oldDelegate];
return newSelectedCharRange;
}
/*
- (void)textViewDidChangeSelection:(NSNotification *)notification;
{
}
- (BOOL)textView:(NSTextView *)textView shouldChangeTextInRange:(NSRange)affectedCharRange replacementString:(NSString *)replacementString;
{
if( textView == hex ) // we're editing in hexadecimal constrain to 0-9, A-F
{
}
else return YES; // we're editing in ASCII
}*/
/*
Raphael Sebbe's code
- (void)textViewDidChangeSelection:(NSNotification *)aNotification
{
BOOL shouldUpdate = NO;
id textView = [aNotification object];
if(textView == _hexText)
{
NSRange hexRange = [textView selectedRange];
NSRange byteRange = [HVHexInterpreter byteRangeFromHexRange:hexRange];
_selectedByteRange = byteRange; shouldUpdate = YES;
}
else if(textView == _asciiText)
{
NSRange asciiRange = [textView selectedRange];
NSRange byteRange = [HVHexInterpreter byteRangeFromAsciiRange:asciiRange];
_selectedByteRange = byteRange; shouldUpdate = YES;
}
if(shouldUpdate)
{
[self updateSelectionFeedback];
[self updateSelectionOffsetTF];
[self updateForms];
}
}
*/
- (NSTextView *)hex
{
return hex;
}
- (NSTextView *)ascii
{
return ascii;
}
@end

View File

@ -0,0 +1,6 @@
#import <AppKit/AppKit.h>
@interface HexTextView : NSTextView
{
}
@end

View File

@ -0,0 +1,29 @@
#import "HexTextView.h"
@implementation HexTextView
- (void)insertText:(id)insertString
{
NSLog( insertString );
[super insertText:insertString];
}
- (void)setSelectedRange:(NSRange)charRange affinity:(NSSelectionAffinity)affinity stillSelecting:(BOOL)flag
{
NSRange newRange = charRange;
// select whole bytes at a time (only if selecting in hex!)
if( self == [[self delegate] hex] )
{
newRange = [self selectionRangeForProposedRange:charRange
granularity:NSSelectByWord];
// newRange.location -= newRange.location % 3;
// newRange.length -= (newRange.length % 3) -2;
NSLog( @"hex selection changed to %@", NSStringFromRange(newRange) );
}
// call the superclass to update the selection
[super setSelectedRange:newRange affinity:affinity stillSelecting:NO];
}
@end

View File

@ -0,0 +1,7 @@
#import <Foundation/Foundation.h>
@interface HexWindow : NSObject
{
}
@end

View File

@ -0,0 +1,5 @@
#import "HexWindow.h"
@implementation HexWindow
@end

View File

@ -0,0 +1,21 @@
#import <AppKit/AppKit.h>
#import "HexEditorDelegate.h"
#import "ResKnifePluginProtocol.h"
#import "ResKnifeResourceProtocol.h"
@interface HexWindowController : NSWindowController <ResKnifePluginProtocol>
{
IBOutlet HexEditorDelegate *hexDelegate;
IBOutlet NSTextView *ascii;
IBOutlet NSTextView *hex;
IBOutlet NSTextView *offset;
id resource;
}
// conform to the ResKnifePluginProtocol with the inclusion of these methods
- (id)initWithResource:(id)newResource;
- (void)refreshData:(NSData *)data;
@end

View File

@ -0,0 +1,57 @@
#import "HexWindowController.h"
#import "HexTextView.h"
@implementation HexWindowController
+ (void)initialize
{
// causes window controller to use HexTextViews wherever it would previously use NSTextView
[HexTextView poseAsClass:[NSTextView class]];
}
- (id)initWithResource:(id)newResource
{
self = [self initWithWindowNibName:@"HexWindow"];
if( self ) [self setWindowFrameAutosaveName:@"Hexadecimal Editor"];
// one instance of your principal class will be created for every resource the user wants to edit (similar to Windows apps)
resource = newResource;
// load the window
// [self setShouldCascadeWindows:YES];
[self window];
return self;
}
- (void)windowDidLoad
{
[super windowDidLoad];
// set up text boxes (this doesn't do what I think it should!)
// [hex setSelectionGranularity:NSSelectByWord];
// insert the resources' data into the text fields
[self refreshData:[(id <ResKnifeResourceProtocol>)resource data]];
// finally, show the window
[self showWindow:self];
}
- (void)refreshData:(NSData *)data;
{
// clear delegates (see HexEditorDelegate class for more info)
id oldDelegate = [hex delegate];
[hex setDelegate:nil];
[ascii setDelegate:nil];
// do stuff with data
[offset setString:[hexDelegate offsetRepresentation:data]];
[hex setString:[hexDelegate hexRepresentation:data]];
[ascii setString:[hexDelegate asciiRepresentation:data]];
// restore delegates
[hex setDelegate:oldDelegate];
[ascii setDelegate:oldDelegate];
}
@end

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist SYSTEM "file://localhost/System/Library/DTDs/PropertyList.dtd">
<plist version="0.9">
<dict>
<key>SupportedTypes</key>
<array>
<string>any</string>
</array>
</dict>
</plist>

View File

@ -0,0 +1,17 @@
#import <Foundation/Foundation.h>
/* Your plug-in's principle class must implement the following methods else it won't be loaded by ResKnife (so neh-neh!) */
@protocol ResKnifePluginProtocol
/*! @function initWithResource:newResource
* @abstract Your plug-in is inited with this call. This allows immediate access to the resource you are about to edit, and with this information you can set up different windows, etc.
*/
- (id)initWithResource:(id)newResource;
/*! @function refreshData:data
* @abstract When your data set is changed via some means other than yourslf, you receive this.
*/
- (void)refreshData:(NSData *)data;
@end

View File

@ -0,0 +1,22 @@
#import <Foundation/Foundation.h>
/* This protocol allows your plug to interrogate a resource to find out information about it. */
@protocol ResKnifeResourceProtocol
- (NSString *)name;
- (void)setName:(NSString *)newName;
- (NSString *)type;
- (void)setType:(NSString *)newType;
- (NSNumber *)resID;
- (void)setResID:(NSNumber *)newResID;
- (NSNumber *)size;
- (void)setSize:(NSNumber *)newSize;
- (NSNumber *)attributes;
- (void)setAttributes:(NSNumber *)newAttributes;
- (BOOL)dirty;
- (void)setDirty:(BOOL)newValue;
- (NSData *)data;
- (void)setData:(NSData *)newData;
@end

View File

@ -38,7 +38,7 @@
F5502C4001C579FF01C57124 = {
isa = PBXFileReference;
name = ResKnifePluginProtocol.h;
path = "PlugIns/ResKnifePluginProtocol.h";
path = "Plug-Ins/ResKnifePluginProtocol.h";
refType = 4;
};
F5502C4101C579FF01C57124 = {
@ -62,27 +62,11 @@
children = (
F5502C4001C579FF01C57124,
F5CDEBAB01FC893201A80001,
F57CEE0C0189C95101A8010B,
F5EF839F020C08E601A80001,
);
isa = PBXGroup;
name = "Plug-Ins";
refType = 4;
};
F57CEE0C0189C95101A8010B = {
children = (
F5CDEBA101FBD4E001A80001,
F5CDEBA201FBD4E001A80001,
F5CDEB9D01FB7C8601A80001,
F5CDEB9E01FB7C8601A80001,
F59CEE9E01A28CA001A8010B,
F59CEE9F01A28CA001A8010B,
F59CEEA201A28D4201A8010B,
F59CEEA301A28D4201A8010B,
F57CEE180189CE8801A8010B,
F5CDEBA901FC86CA01A80001,
);
isa = PBXGroup;
name = "Hex Editor";
path = "";
refType = 4;
};
F57CEE0D0189C95101A8010B = {
@ -151,12 +135,12 @@
F57CEE0F0189C95101A8010B = {
buildActionMask = 2147483647;
files = (
F59CEEA001A28CA101A8010B,
F59CEEA401A28D4301A8010B,
F5CDEBA301FBD4E101A80001,
F5CDEBAD01FC893301A80001,
F5CDEBAE01FC90AD01A80001,
F583B47802083A0A01A80001,
F5EF83AA020C08E601A80001,
F5EF83AB020C08E601A80001,
F5EF83AC020C08E601A80001,
F5EF83AD020C08E601A80001,
);
isa = PBXHeadersBuildPhase;
name = Headers;
@ -164,8 +148,8 @@
F57CEE100189C95101A8010B = {
buildActionMask = 2147483647;
files = (
F57CEE190189CE8901A8010B,
F5CDEBAA01FC86CB01A80001,
F5EF83AF020C08E601A80001,
F5EF83C6020C162C01A80001,
);
isa = PBXResourcesBuildPhase;
name = "Bundle Resources";
@ -173,10 +157,10 @@
F57CEE110189C95101A8010B = {
buildActionMask = 2147483647;
files = (
F59CEEA101A28CA101A8010B,
F59CEEA501A28D4301A8010B,
F5CDEBA401FBD4E101A80001,
F583B47902083A0A01A80001,
F5EF83B0020C08E601A80001,
F5EF83B1020C08E601A80001,
F5EF83B2020C08E601A80001,
F5EF83B3020C08E601A80001,
);
isa = PBXSourcesBuildPhase;
name = Sources;
@ -196,78 +180,6 @@
isa = PBXRezBuildPhase;
name = "ResourceManager Resources";
};
F57CEE180189CE8801A8010B = {
isa = PBXFileReference;
name = HexWindow.nib;
path = "Cocoa/PlugIns/Hex Editor/HexWindow.nib";
refType = 2;
};
F57CEE190189CE8901A8010B = {
fileRef = F57CEE180189CE8801A8010B;
isa = PBXBuildFile;
settings = {
};
};
F583B47802083A0A01A80001 = {
fileRef = F5CDEB9D01FB7C8601A80001;
isa = PBXBuildFile;
settings = {
};
};
F583B47902083A0A01A80001 = {
fileRef = F5CDEB9E01FB7C8601A80001;
isa = PBXBuildFile;
settings = {
};
};
F59CEE9E01A28CA001A8010B = {
isa = PBXFileReference;
name = HexWindow.h;
path = "PlugIns/Hex Editor/HexWindow.h";
refType = 4;
};
F59CEE9F01A28CA001A8010B = {
isa = PBXFileReference;
name = HexWindow.m;
path = "PlugIns/Hex Editor/HexWindow.m";
refType = 4;
};
F59CEEA001A28CA101A8010B = {
fileRef = F59CEE9E01A28CA001A8010B;
isa = PBXBuildFile;
settings = {
};
};
F59CEEA101A28CA101A8010B = {
fileRef = F59CEE9F01A28CA001A8010B;
isa = PBXBuildFile;
settings = {
};
};
F59CEEA201A28D4201A8010B = {
isa = PBXFileReference;
name = HexWindowController.h;
path = "PlugIns/Hex Editor/HexWindowController.h";
refType = 4;
};
F59CEEA301A28D4201A8010B = {
isa = PBXFileReference;
name = HexWindowController.m;
path = "PlugIns/Hex Editor/HexWindowController.m";
refType = 4;
};
F59CEEA401A28D4301A8010B = {
fileRef = F59CEEA201A28D4201A8010B;
isa = PBXBuildFile;
settings = {
};
};
F59CEEA501A28D4301A8010B = {
fileRef = F59CEEA301A28D4201A8010B;
isa = PBXBuildFile;
settings = {
};
};
F5B5880F0156D2A601000001 = {
buildStyles = (
F5EBF6B801573EC201000001,
@ -2331,58 +2243,10 @@
isa = PBXRezBuildPhase;
name = "ResourceManager Resources";
};
F5CDEB9D01FB7C8601A80001 = {
isa = PBXFileReference;
name = HexTextView.h;
path = "PlugIns/Hex Editor/HexTextView.h";
refType = 4;
};
F5CDEB9E01FB7C8601A80001 = {
isa = PBXFileReference;
name = HexTextView.m;
path = "PlugIns/Hex Editor/HexTextView.m";
refType = 4;
};
F5CDEBA101FBD4E001A80001 = {
isa = PBXFileReference;
name = HexEditorDelegate.h;
path = "PlugIns/Hex Editor/HexEditorDelegate.h";
refType = 4;
};
F5CDEBA201FBD4E001A80001 = {
isa = PBXFileReference;
name = HexEditorDelegate.m;
path = "PlugIns/Hex Editor/HexEditorDelegate.m";
refType = 4;
};
F5CDEBA301FBD4E101A80001 = {
fileRef = F5CDEBA101FBD4E001A80001;
isa = PBXBuildFile;
settings = {
};
};
F5CDEBA401FBD4E101A80001 = {
fileRef = F5CDEBA201FBD4E001A80001;
isa = PBXBuildFile;
settings = {
};
};
F5CDEBA901FC86CA01A80001 = {
isa = PBXFileReference;
name = SupportedTypes.plist;
path = "Cocoa/PlugIns/Hex Editor/SupportedTypes.plist";
refType = 2;
};
F5CDEBAA01FC86CB01A80001 = {
fileRef = F5CDEBA901FC86CA01A80001;
isa = PBXBuildFile;
settings = {
};
};
F5CDEBAB01FC893201A80001 = {
isa = PBXFileReference;
name = ResKnifeResourceProtocol.h;
path = "PlugIns/ResKnifeResourceProtocol.h";
path = "Plug-Ins/ResKnifeResourceProtocol.h";
refType = 4;
};
F5CDEBAC01FC893201A80001 = {
@ -2432,6 +2296,143 @@
isa = PBXBuildStyle;
name = Deployment;
};
F5EF839F020C08E601A80001 = {
children = (
F5EF83A0020C08E601A80001,
F5EF83A1020C08E601A80001,
F5EF83A2020C08E601A80001,
F5EF83A3020C08E601A80001,
F5EF83A4020C08E601A80001,
F5EF83A5020C08E601A80001,
F5EF83A7020C08E601A80001,
F5EF83A8020C08E601A80001,
F5EF83A9020C08E601A80001,
F5EF83C5020C162B01A80001,
);
isa = PBXGroup;
name = "Hex Editor";
path = "Plug-Ins/Hex Editor";
refType = 4;
};
F5EF83A0020C08E601A80001 = {
isa = PBXFileReference;
path = HexEditorDelegate.h;
refType = 4;
};
F5EF83A1020C08E601A80001 = {
isa = PBXFileReference;
path = HexEditorDelegate.m;
refType = 4;
};
F5EF83A2020C08E601A80001 = {
isa = PBXFileReference;
path = HexTextView.h;
refType = 4;
};
F5EF83A3020C08E601A80001 = {
isa = PBXFileReference;
path = HexTextView.m;
refType = 4;
};
F5EF83A4020C08E601A80001 = {
isa = PBXFileReference;
path = HexWindow.h;
refType = 4;
};
F5EF83A5020C08E601A80001 = {
isa = PBXFileReference;
path = HexWindow.m;
refType = 4;
};
F5EF83A7020C08E601A80001 = {
isa = PBXFileReference;
path = HexWindowController.h;
refType = 4;
};
F5EF83A8020C08E601A80001 = {
isa = PBXFileReference;
path = HexWindowController.m;
refType = 4;
};
F5EF83A9020C08E601A80001 = {
isa = PBXFileReference;
path = SupportedTypes.plist;
refType = 4;
};
F5EF83AA020C08E601A80001 = {
fileRef = F5EF83A0020C08E601A80001;
isa = PBXBuildFile;
settings = {
};
};
F5EF83AB020C08E601A80001 = {
fileRef = F5EF83A2020C08E601A80001;
isa = PBXBuildFile;
settings = {
};
};
F5EF83AC020C08E601A80001 = {
fileRef = F5EF83A4020C08E601A80001;
isa = PBXBuildFile;
settings = {
};
};
F5EF83AD020C08E601A80001 = {
fileRef = F5EF83A7020C08E601A80001;
isa = PBXBuildFile;
settings = {
};
};
F5EF83AF020C08E601A80001 = {
fileRef = F5EF83A9020C08E601A80001;
isa = PBXBuildFile;
settings = {
};
};
F5EF83B0020C08E601A80001 = {
fileRef = F5EF83A1020C08E601A80001;
isa = PBXBuildFile;
settings = {
};
};
F5EF83B1020C08E601A80001 = {
fileRef = F5EF83A3020C08E601A80001;
isa = PBXBuildFile;
settings = {
};
};
F5EF83B2020C08E601A80001 = {
fileRef = F5EF83A5020C08E601A80001;
isa = PBXBuildFile;
settings = {
};
};
F5EF83B3020C08E601A80001 = {
fileRef = F5EF83A8020C08E601A80001;
isa = PBXBuildFile;
settings = {
};
};
F5EF83C3020C10C601A80001 = {
isa = PBXFileReference;
name = HexWindow.nib;
path = English.lproj/HexWindow.nib;
refType = 4;
};
F5EF83C5020C162B01A80001 = {
children = (
F5EF83C3020C10C601A80001,
);
isa = PBXVariantGroup;
name = HexWindow.nib;
refType = 4;
};
F5EF83C6020C162C01A80001 = {
fileRef = F5EF83C5020C162B01A80001;
isa = PBXBuildFile;
settings = {
};
};
};
rootObject = F5B5880F0156D2A601000001;
}