-
Notifications
You must be signed in to change notification settings - Fork 701
/
DataSources.mm
313 lines (253 loc) · 9.26 KB
/
DataSources.mm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
/*
* DataSources.mm
* MachOView
*
* Created by psaghelyi on 15/06/2010.
*
*/
#import "Common.h"
#import "DataSources.h"
#import "DataController.h"
#import "Document.h"
#import "Layout.h"
NSString * const MVScannerErrorMessage = @"NSScanner error";
//============================================================================
@implementation MVDataSourceTree
#pragma mark NSOutlineView must-have delegates
- (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item
{
if (item == nil)
{
return 1;
}
MVNode * node = item;
return node.numberOfChildren;
}
//----------------------------------------------------------------------------
- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item
{
if (item == nil)
{
return YES;
}
MVNode * node = item;
return (node.numberOfChildren > 0);
}
//----------------------------------------------------------------------------
- (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item
{
MVDocument * document = [[[outlineView window] windowController] document];
if (item == nil)
{
return document.dataController.rootNode;
}
MVNode * node = item;
return [node childAtIndex:index];
}
//----------------------------------------------------------------------------
- (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
{
if (item == nil)
{
return @"-";
}
MVNode * node = item;
if (node.details != nil && node.detailsOffset == 0)
{
return [@"*" stringByAppendingString:node.caption];
}
return node.caption;
}
//----------------------------------------------------------------------------
@end
//============================================================================
@implementation MVDataSourceDetails
#pragma mark NSTableView must-have delegates
- (NSInteger)numberOfRowsInTableView:(NSTableView *)aTableView
{
MVDocument * document = [[[aTableView window] windowController] document];
MVNode * selectedNode = document.dataController.selectedNode;
// if there is no details, then provide binary dump
if (selectedNode.details == nil)
{
NSInteger numRows = selectedNode.dataRange.length / 16;
if (selectedNode.dataRange.length % 16 != 0)
{
++numRows;
}
return numRows;
}
return selectedNode.details.rowCountToDisplay;
}
//----------------------------------------------------------------------------
- (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex
{
MVDocument * document = [[[aTableView window] windowController] document];
MVNode * selectedNode = document.dataController.selectedNode;
// if it is closing...
if (document == nil)
{
return nil;
}
NSUInteger colIndex = [[aTableView tableColumns] indexOfObject:aTableColumn];
//NSLog (@"queried (%d, %d)", rowIndex, colIndex);
// if it has no details then show binary data at given range
if (selectedNode.details == nil)
{
NSUInteger offset = selectedNode.dataRange.location + rowIndex * 16;
// file offset
if (colIndex == OFFSET_COLUMN)
{
NSString * cellContent = [NSString stringWithFormat:@"%.8lX", offset];
if ([document isRVA] == YES)
{
MVLayout *layout = [selectedNode.userInfo objectForKey:MVLayoutUserInfoKey];
return [layout convertToRVA:cellContent];
}
return cellContent;
}
// binary data
uint8_t buffer[17] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
NSUInteger len = MIN(selectedNode.dataRange.length - rowIndex * 16, (NSUInteger)16);
memcpy(buffer, (uint8_t *)[document.dataController.fileData bytes] + offset, len);
if (colIndex == DATA_LO_COLUMN)
{
NSUInteger index = (len > 8 ? 8 : len);
return [[NSString stringWithFormat:@"%.2X %.2X %.2X %.2X %.2X %.2X %.2X %.2X ",
buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5], buffer[6], buffer[7]]
substringToIndex:index*3];
}
if (colIndex == DATA_HI_COLUMN)
{
NSUInteger index = (len > 8 ? len - 8 : 0);
return [[NSString stringWithFormat:@"%.2X %.2X %.2X %.2X %.2X %.2X %.2X %.2X ",
buffer[8], buffer[9], buffer[10], buffer[11], buffer[12], buffer[13], buffer[14], buffer[15]]
substringToIndex:index*3];
}
// textual data (where possible)
for (NSUInteger i = 0; i < len; ++i)
{
// keep the output in ASCII
if (buffer[i] < 32 || buffer[i] > 126)
{
buffer[i] = '.';
}
}
return NSSTRING(buffer);
}
// if it has descripion then show it
MVRow * row = [selectedNode.details getRowToDisplay:rowIndex];
if (row != nil)
{
NSString * cellContent = [row columnAtIndex:colIndex];
// special column is the offset column:
// if RVA is selected then subtitute the content on the fly
if (colIndex == OFFSET_COLUMN && [cellContent length] > 0)
{
if ([document isRVA] == YES)
{
MVLayout *layout = [selectedNode.userInfo objectForKey:MVLayoutUserInfoKey];
cellContent = [layout convertToRVA:cellContent];
}
}
// put formatting on display text
NSColor * color = [row.attributes objectForKey:MVTextColorAttributeName];
if (color != nil)
{
NSDictionary * attributes = [NSDictionary dictionaryWithObject:color forKey:NSForegroundColorAttributeName];
return [[NSAttributedString alloc] initWithString:cellContent
attributes:attributes];
}
return cellContent;
}
return nil;
}
//----------------------------------------------------------------------------
- (void)tableView:(NSTableView *)aTableView setObjectValue:(id)anObject forTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex
{
BOOL scanResult;
uint64_t fileOffset;
NSUInteger colIndex = [[aTableView tableColumns] indexOfObject:aTableColumn];
MVDocument * document = [[[aTableView window] windowController] document];
NSString * cellContent = ([anObject isKindOfClass:[NSAttributedString class]] ? [anObject string] : anObject);
NSScanner * scanner = [NSScanner scannerWithString:cellContent];
MVNode * selectedNode = document.dataController.selectedNode;
if (selectedNode.details != nil)
// option1: plain hex value
{
MVRow * row = [selectedNode.details getRowToDisplay:rowIndex];
if (row == nil)
{
return;
}
// find out file offset from the offset column
scanResult = [[NSScanner scannerWithString:row.columns.offsetStr] scanHexLongLong:&fileOffset];
if (scanResult == NO)
{
NSAssert(NO, MVScannerErrorMessage);
return;
}
NSRange dataRange = NSMakeRange(fileOffset, [cellContent length] / 2);
if (dataRange.length <= sizeof(uint64_t))
{
uint64_t value;
scanResult = [scanner scanHexLongLong:&value];
if (scanResult == NO)
{
NSAssert(NO, MVScannerErrorMessage);
return;
}
[document.dataController.fileData replaceBytesInRange:dataRange withBytes:&value];
}
else
{
// create a place holder for new value
NSAssert ([cellContent length] % 2 == 0, @"cell content length must be even");
NSMutableData * mdata = [NSMutableData dataWithCapacity:dataRange.length];
static char buf[3];
char const * orgstr = CSTRING(cellContent);
for (NSUInteger s = 0; s < [cellContent length]; s += 2)
{
buf[0] = orgstr[s];
buf[1] = orgstr[s+1];
unsigned long value = strtoul (buf, NULL, 16);
[mdata appendBytes:&value length:sizeof(uint8_t)];
}
// replace data with the new value
[document.dataController.fileData replaceBytesInRange:dataRange withBytes:[mdata bytes]];
}
// update the cell content to indicate changes
//================================================
selectedNode.detailsOffset = 0;
[selectedNode.details updateCellContentTo:cellContent atRow:rowIndex andCol:colIndex];
[selectedNode.details setAttributesForRowIndex:rowIndex:MVTextColorAttributeName,[NSColor redColor],nil];
}
else
// option2: group of bytes
{
// find out file offset from the row index
fileOffset = selectedNode.dataRange.location + 16 * rowIndex + 8 * (colIndex == DATA_HI_COLUMN);
// create a place holder for new value
NSMutableData * mdata = [NSMutableData dataWithCapacity:[cellContent length] / 3]; // each element = one byte plus space
// fill in placeholder
while ([scanner isAtEnd] == NO)
{
unsigned value;
scanResult = [scanner scanHexInt:&value];
if (scanResult == NO)
{
NSAssert(NO, MVScannerErrorMessage);
return;
}
[mdata appendBytes:&value length:sizeof(uint8_t)];
}
// replace data with the new value
[document.dataController.fileData replaceBytesInRange:NSMakeRange(fileOffset, [mdata length])
withBytes:[mdata bytes]];
// do not need to update cell content...
}
// set document to dirty
[document updateChangeCount:NSChangeDone];
}
//----------------------------------------------------------------------------
@end