#import "BWSideView.h"
#import "BWGameData.h"
#import "Variables.h"

@implementation BWSideView : NSMatrix

# pragma mark -

- (void)awakeFromNib
{
	tiles = [[NSMutableArray alloc] initWithCapacity:64];
	tilesItems = [[NSMutableArray alloc] initWithCapacity:16];
	tilesEnemies = [[NSMutableArray alloc] initWithCapacity:64];
	
	mapScale = 1;
	[self setRefusesFirstResponder:YES];
	//[scrollView setLineScroll:(mapScale * 16)];
	//[[[self superview] superview] setHasVerticalScroller:YES];
	[self setCellSize:NSMakeSize( (16 * mapScale) , (16 * mapScale) )];
	[self sizeToCells];
}

- (void)open:(NSMutableData *)theRom
{
	rom = theRom;
	// The tiles must be loaded according to the area's tileset bit
	
	/* Code for Pointer Output //
	int i;
	unsigned int a1,a2;
	ptr = [rom bytes] + 0x45A1 + 0x10;
	
	for (i=0; i<0x3F; i++) {
		a1 = *ptr | *(ptr + 1) << 8;
		a2 = a1 + 0x48A0 - 0x7000;
		printf("%02X%02X -> %04X\n",*ptr,*(ptr+1),a2);
		ptr += 2;
	}
	
	return;
	*/
	
	int i;
	for (i=0; i<0x40; i++) {		// Loop for all 8 worlds
		enemyAssoc[0][i] = 0;
	}
	
	cw = 0;
	
	[self loadPalettes];
	[self loadTileMappings];
	[self loadTileCodes];
	[self loadAreaData];
	[self loadTilesEnemies];
	[self loadEnemyData];
	[self loadAssociations];
	
	[self renewRows:0x0D columns:0x40];
	[self sizeToCells];
	
	[self changeArea:nil];
}

# pragma mark -

- (void)loadPalettes
{
	int i,j;
	unsigned int palOffset[8] = { 0x400E , 0x400E , 0x800E , 0x800E , 0 , 0 , 0 , 0 };	// 400E = West Hyrule / 800E = East Hyrule //
	
	for (i=0; i<2; i++) {
		ptr = [rom bytes] + palOffset[i] + 0x10;
		for (j=0; j<0xE0; j++) {
			palettes[i][j] = *ptr++;
		}
	}
}

- (void)loadTileMappings
{
	int i,j,k;
	int offset;
	unsigned int tmOffset[8] = { 0x4500 , 0x4500 , 0x800E , 0x800E , 0 , 0 , 0 , 0 };
		
	for (i=0; i<2; i++) {
		for (j=0; j<4; j++) {
			ptr = [rom bytes] + tmOffset[i] + (j * 2) + 0x10;
			offset = *ptr | *(ptr + 1) << 8;
			offset -= 0x8000;
			offset += 0x4000 * 1;
			ptr = [rom bytes] + offset + 0x10;
			for (k=0; k<0x100; k++) {
				tileMappings[i][(j*0x100)+k] = *ptr++;
				//if (j<4) NSLog(@"tm : %X",tileMappings[cw][(j*0x100)+k]);
			}
		}
	}
}

- (void)loadTileCodes
{
	int i,j;
	unsigned int tcOffset1[8] = { 0x41B7 , 0x41B7 , 0 , 0 , 0 , 0 , 0 , 0 };
	unsigned int tcOffset2[8] = { 0x41D5 , 0x41D5 , 0 , 0 , 0 , 0 , 0 , 0 };
	
	for (i=0; i<2; i++) {
		ptr = [rom bytes] + tcOffset1[i] + 0x10;		// Set 0
		for (j=2; j<0x20; j++) {
			tileCodes[i][0][j] = *ptr++;
		}
		
		ptr = [rom bytes] + tcOffset2[i] + 0x10;		// Set 1
		for (j=2; j<0x20; j++) {
			tileCodes[i][1][j] = *ptr++;
		}
	}
}

- (void)loadTiles
{
	[tiles removeAllObjects];
	
	NSImage *tileFinal;
	NSMutableArray *tsa = [NSMutableArray arrayWithCapacity:4];
	
	unsigned int offset;
	unsigned char palCode;
	unsigned char tm;
	struct sCol tp[4];
	
	int i,j,x;
	for (i=0; i<4; i++) {
		
		for (j=0; j<0x100; j+=4) {
			
			palCode = (areaData[cw][cd][3] & 0x38) >> 3;
			palCode *= 0x10;
			palCode += (i * 4);
			
			for (x=0; x<4; x++) {
				if (palettes[cw][palCode+x] == 0xFF) tp[x] = nesPal[0x40];
				else tp[x] = nesPal[palettes[cw][palCode+x]];
			}
			
			for (x=0; x<4; x++) {
				tm = tileMappings[cw][(i*0x100)+j+x];
				offset = 0x20000 + (0x2000 * 1) + 0x1000 + (tm * 0x10) + 0x10;
				[tsa addObject:[self tileWithOffset:offset palette:tp]];
			}
			
			tileFinal = [self tileBlockWithTiles:tsa];
			[tiles addObject:tileFinal];
			[tsa removeAllObjects];
		}
	}
}

- (void)loadTilesItems
{
	[tilesItems removeAllObjects];
	
	NSImage *tileFinal;
	NSMutableArray *tsa = [NSMutableArray arrayWithCapacity:4];
	
	int max = 0x21;
	int os;
	const unsigned char *palPtr;
	
	int i,x;
	for (i=0; i<max; i++) {
		
		palPtr = [rom bytes] + 0x40AE;  // Palette Pointer
		
		struct sCol tp[4];				// Palette Array
		for (x=0; x<4; x++) {
			if (*(palPtr + x) == 0xFF) tp[x] = nesPal[0x40];
			else tp[x] = nesPal[*(palPtr + x)];
		}
		
		for (x=0; x<4; x++) {
			os = 0x20010 + 0x2000 + (tmi[(i*4)+x] * 0x10);
			if (i == 13) os += 0x2000;
			
			if (tmi[(i*4)+x] == 0) [tsa addObject:[self emptyTile]];
			else [tsa addObject:[self tileWithOffset:os palette:tp]];
			
			// Conditions here...
			
			if ((i == 9) && (x == 2)) [self flipTile:[tsa lastObject] type:1];  // Magic Container
			if ((i == 9) && (x == 3)) [self flipTile:[tsa lastObject] type:1];  // Magic Container
			if ((i == 10) && (x == 2)) [self flipTile:[tsa lastObject] type:1]; // Heart Container
			if ((i == 10) && (x == 3)) [self flipTile:[tsa lastObject] type:1]; // Heart Container
			if ((i == 13) && (x == 2)) [self flipTile:[tsa lastObject] type:1]; // Kidnapped Child
			if ((i == 13) && (x == 3)) [self flipTile:[tsa lastObject] type:1]; // Kidnapped Child
			if ((i == 14) && (x == 2)) [self flipTile:[tsa lastObject] type:1]; // Trophy
			if ((i == 14) && (x == 3)) [self flipTile:[tsa lastObject] type:1]; // Trophy
			if ((i == 15) && (x == 2)) [self flipTile:[tsa lastObject] type:1]; // Medicine
			if ((i == 15) && (x == 3)) [self flipTile:[tsa lastObject] type:1]; // Medicine
		}
		
		tileFinal = [self tileBlockWithTiles:tsa];
		[tilesItems addObject:tileFinal];
		[tsa removeAllObjects];
	}
}

- (void)loadTilesEnemies
{
	[tilesEnemies removeAllObjects];
	
	int max = 46;
	
	NSImage *tileFinal;
	NSMutableArray *tsa = [NSMutableArray arrayWithCapacity:6];
	NSMutableArray *tilesTemp = [NSMutableArray arrayWithCapacity:32];
	
	int os;
	const unsigned char *palPtr;
	const unsigned char *bitPtr;
	
	unsigned short palType[4] = { 0x409E , 0x40A2 , 0x40A6 , 0x40BA };
	
	max = 0x25;		// 0x25 = 37 Decimal
	int i,x;
	for (i=0; i<max; i++) {
		
		//palPtr = [rom bytes] + 0x40B2;  // Palette Pointer
		bitPtr = [rom bytes] + 0x5431 + 0xB4 + tmePalOffset[i]; //NSLog(@"bitType : %X",*bitPtr);
		palPtr = [rom bytes] + palType[((*bitPtr & 0xC0) >> 6)] + 0x10;
		if (i == 1) palPtr = [rom bytes] + 0x40A6 + 0x10;
		//NSLog(@"palType : %X",((*bitPtr & 0xC0) >> 6));
		//NSLog(@"----");
		
		struct sCol tp[4];				// Palette Array
		for (x=0; x<4; x++) {
			if (*(palPtr + x) == 0xFF) tp[x] = nesPal[0x40];
			else tp[x] = nesPal[*(palPtr + x)];
		}
		
		for (x=0; x<4; x++) {
			os = 0x20010 + (0x2000 * 1) + (tme[(i*4)+x] * 0x10);  // Set? Bank?
			
			if (tme[(i*4)+x] == 0) {
				[tsa addObject:[self emptyTile]];	//NSLog(@"Empty");
			} else {
				[tsa addObject:[self tileWithOffset:os palette:tp]];	//NSLog(@"%X",os);
			}
			
			// Conditions here...
			
			if ((i == 4) && (x == 3)) [self flipTile:[tsa lastObject] type:1];  // Myu
			if ((i == 5) && (x == 2)) [self flipTile:[tsa lastObject] type:1];  // Bit Upper-Right
			if ((i == 5) && (x == 3)) [self flipTile:[tsa lastObject] type:1];  // Bit Lower-Right
			if ((i == 6) && (x == 2)) [self flipTile:[tsa lastObject] type:1];  // Bot Upper-Right
			if ((i == 6) && (x == 3)) [self flipTile:[tsa lastObject] type:1];  // Bot Lower-Right
			if ((i == 8) && (x == 2)) [self flipTile:[tsa lastObject] type:1];  // Ache Upper-Right
			if ((i == 8) && (x == 3)) [self flipTile:[tsa lastObject] type:1];  // Ache Lower-Right
		}
		
		tileFinal = [self tileBlockWithTiles:tsa];
		[tilesTemp addObject:tileFinal];
		[tsa removeAllObjects];
	}
	
	for (i=0; i<0x23; i++) {
		[tsa removeAllObjects];
		switch (i) {
			case 0x00: [tilesEnemies addObject:[tilesTemp objectAtIndex:0]]; break;  // Fairy
			case 0x01: [tilesEnemies addObject:[tilesTemp objectAtIndex:1]]; break;  // Jar
			
			case 0x02:
			[tsa addObject:[tilesTemp objectAtIndex:2]];
			[tsa addObject:[tilesTemp objectAtIndex:3]];
			[tsa addObject:[tilesTemp objectAtIndex:2]];
			[tilesEnemies addObject:[self graphicWithBlocks:tsa type:0x31]]; break;		// Locked Door (3 x 1)
			
			case 0x03: [tilesEnemies addObject:[tilesTemp objectAtIndex:4]]; break;		// Myu
			case 0x04: [tilesEnemies addObject:[tilesTemp objectAtIndex:5]]; break;		// Bit
			case 0x05: [tilesEnemies addObject:[tilesTemp objectAtIndex:6]]; break;		// Bot
			case 0x06: [tilesEnemies addObject:[tilesTemp objectAtIndex:7]]; break;		// Moa
			case 0x07: [tilesEnemies addObject:[tilesTemp objectAtIndex:8]]; break;		// Ache
			
			case 0x08: [tilesEnemies addObject:[NSImage imageNamed:@"Null"]]; break;	// Null
			case 0x09: [tilesEnemies addObject:[NSImage imageNamed:@"Null"]]; break;	// Null
			
			case 0x0A:
			[tsa addObject:[tilesTemp objectAtIndex:9]];
			[tsa addObject:[tilesTemp objectAtIndex:0xA]];
			[tilesEnemies addObject:[self graphicWithBlocks:tsa type:0x21]]; break;		// Acheman (2 x 1)
			
			case 0x0B: [tilesEnemies addObject:[NSImage imageNamed:@"BubbleGenerator"]]; break; // Bubble Gen.
			case 0x0C: [tilesEnemies addObject:[NSImage imageNamed:@"RockGenerator"]]; break;   // Rock Gen.
			
			case 0x0D: [tilesEnemies addObject:[tilesTemp objectAtIndex:0xB]]; break;	// Deeler Red
			case 0x0E: [tilesEnemies addObject:[tilesTemp objectAtIndex:0xC]]; break;	// Deeler Blue
			
			case 0x0F: [tilesEnemies addObject:[tilesTemp objectAtIndex:0xD]]; break;	// Bago Gen.
			case 0x10: [tilesEnemies addObject:[tilesTemp objectAtIndex:0xD]]; break;	// Bago Bago
			
			case 0x11: [tilesEnemies addObject:[tilesTemp objectAtIndex:0xE]]; break;	// Octorok Jumping
			case 0x12: [tilesEnemies addObject:[tilesTemp objectAtIndex:0xE]]; break;	// Octorok Moving
			
			case 0x13:
			for (x=0; x<2; x++) {
				[tsa addObject:[tilesTemp objectAtIndex:0xF]];
				[tsa addObject:[tilesTemp objectAtIndex:0x10]];
				[tsa addObject:[tilesTemp objectAtIndex:0x11]];
			}
			[tilesEnemies addObject:[self graphicWithBlocks:tsa type:0x32]]; break; // Elevator (3 x 2)
			
			case 0x14:
			[tsa addObject:[tilesTemp objectAtIndex:0x12]];
			[tsa addObject:[tilesTemp objectAtIndex:0x15]];
			[tilesEnemies addObject:[self graphicWithBlocks:tsa type:0x21]]; break; // Moblin Orange (2 x 1)
			
			case 0x15:
			[tsa addObject:[tilesTemp objectAtIndex:0x13]];
			[tsa addObject:[tilesTemp objectAtIndex:0x16]];
			[tilesEnemies addObject:[self graphicWithBlocks:tsa type:0x21]]; break; // Moblin Red (2 x 1)
			
			case 0x16:
			[tsa addObject:[tilesTemp objectAtIndex:0x14]];
			[tsa addObject:[tilesTemp objectAtIndex:0x17]];
			[tilesEnemies addObject:[self graphicWithBlocks:tsa type:0x21]]; break; // Moblin Blue (2 x 1)
			
			case 0x17:
			[tsa addObject:[tilesTemp objectAtIndex:0x18]];
			[tsa addObject:[tilesTemp objectAtIndex:0x1D]];
			[tilesEnemies addObject:[self graphicWithBlocks:tsa type:0x21]]; break; // Daira Orange (2 x 1)
			
			case 0x18:
			[tsa addObject:[tilesTemp objectAtIndex:0x19]];
			[tsa addObject:[tilesTemp objectAtIndex:0x1E]];
			[tilesEnemies addObject:[self graphicWithBlocks:tsa type:0x21]]; break; // Daira Red (2 x 1)
			
			case 0x19:
			[tsa addObject:[tilesTemp objectAtIndex:0x1A]];
			[tsa addObject:[tilesTemp objectAtIndex:0x1D]];
			[tilesEnemies addObject:[self graphicWithBlocks:tsa type:0x21]]; break; // Daira Red (2 x 1)
			
			case 0x1A:
			[tsa addObject:[tilesTemp objectAtIndex:0x1B]];
			[tsa addObject:[tilesTemp objectAtIndex:0x1E]];
			[tilesEnemies addObject:[self graphicWithBlocks:tsa type:0x21]]; break; // Daira Red (2 x 1)
			
			case 0x1B:
			[tsa addObject:[tilesTemp objectAtIndex:0x1C]];
			[tsa addObject:[tilesTemp objectAtIndex:0x1F]];
			[tilesEnemies addObject:[self graphicWithBlocks:tsa type:0x21]]; break; // Daira Red (2 x 1)
			
			case 0x1C: [tilesEnemies addObject:[tilesTemp objectAtIndex:0x20]]; break;	// Lowder
			
			case 0x1D: [tilesEnemies addObject:[tilesTemp objectAtIndex:0x21]]; break;	// Moby Gen.
			case 0x1E: [tilesEnemies addObject:[tilesTemp objectAtIndex:0x21]]; break;	// Moby
			
			case 0x1F: [tilesEnemies addObject:[tilesTemp objectAtIndex:0x22]]; break;	// Megmet
			
			case 0x20:
			[tsa addObject:[tilesTemp objectAtIndex:0x23]];
			[tsa addObject:[tilesTemp objectAtIndex:0x24]];
			[tsa addObject:[tilesTemp objectAtIndex:0x24]];
			[tsa addObject:[tilesTemp objectAtIndex:0x24]];
			[tsa addObject:[tilesTemp objectAtIndex:0x24]];
			[tilesEnemies addObject:[self graphicWithBlocks:tsa type:0x51]]; break;		// Geldarm (5 x 1)
			
			case 0x21:
			[tsa addObject:[tilesTemp objectAtIndex:0x12]];
			[tsa addObject:[tilesTemp objectAtIndex:0x15]];
			[tilesEnemies addObject:[self graphicWithBlocks:tsa type:0x21]]; break;		// Dumb Moblin Gen.
			
			case 0x22:
			[tsa addObject:[tilesTemp objectAtIndex:0x12]];
			[tsa addObject:[tilesTemp objectAtIndex:0x15]];
			[tilesEnemies addObject:[self graphicWithBlocks:tsa type:0x21]]; break;		// Dumb Moblin
			
			break;
		}
	}
	/*
	NSString *path;
	NSData *ddd;
	for (i=0; i<[tilesEnemies count]; i++) {
		path = [[NSString stringWithFormat:@"~/Desktop/TestTiles/enemy%d.tiff",i] stringByExpandingTildeInPath];
		ddd = [[tilesEnemies objectAtIndex:i] TIFFRepresentationUsingCompression:NSTIFFCompressionLZW factor:1.0];
		[ddd writeToFile:path atomically:YES];
	}
	*/
}

/* Types (in hex)

21 = 2 Blocks - Vertical (Acheman, Moblin, Daira, Goriya, etc...)
31 = 3 Blocks - Vertical (Locked Door)
41 = 4 Blocks - Vertical (Geldarm)
12 = 2 Blocks - Horizontal (Basilisk)
22 = 2 Blocks High, 2 Blocks Wide (Scorpion)
32 = 3 Blocks High, 2 Blocks Wide (Elevator)

*/

# pragma mark -

- (NSImage *)graphicWithBlocks:(NSArray *)blocks type:(unsigned char)size
{
	int sr = mapScale;			// Scale Ratio
	int w = size & 0x0F;		//NSLog(@"w = %d",w);
	int h = (size & 0xF0) >> 4;	//NSLog(@"h = %d",h);
	int width = w * 16 * sr;
	int height = h * 16 * sr;
	
	NSImage *tileGraphic = [[NSImage alloc] initWithSize:NSMakeSize(width,height)];
	NSBitmapImageRep *tileRep = [[NSBitmapImageRep alloc] 
				initWithBitmapDataPlanes:NULL
							  pixelsWide:width
							  pixelsHigh:height
						   bitsPerSample:8
						 samplesPerPixel:4
								hasAlpha:YES
								isPlanar:NO
						  colorSpaceName:NSDeviceRGBColorSpace
							 bytesPerRow:(width * 4)
							bitsPerPixel:32];
	
	[tileGraphic addRepresentation:tileRep];
	
	NSImage *imageToDraw;
	NSPoint pt;
	
	[tileGraphic lockFocus];
	int i,j;
	for (i=0; i<w; i++) {
		for (j=0; j<h; j++) {
			imageToDraw = [blocks objectAtIndex:j+(i*h)];
			pt.y = (h - j - 1) * [imageToDraw size].height * sr;
			pt.x = i * [imageToDraw size].width * sr;
			[imageToDraw drawAtPoint:pt fromRect:NSZeroRect operation:1 fraction:1.0];
		}
	}
	[tileGraphic unlockFocus];
	
	return tileGraphic;
}

- (NSImage *)tileBlockWithTiles:(NSArray *)tsa
{
	int sr = mapScale;		// Scale Ratio
	int wh = sr * 8 * 2;
	
	unsigned char *pf;
	
	NSImage *oneTile;
	NSImage *tileFinal = [[NSImage alloc] initWithSize:NSMakeSize(wh,wh)];
	NSBitmapImageRep *tileRep = [[NSBitmapImageRep alloc] 
				initWithBitmapDataPlanes:NULL
							  pixelsWide:wh
							  pixelsHigh:wh
						   bitsPerSample:8
						 samplesPerPixel:4
								hasAlpha:YES
								isPlanar:NO
						  colorSpaceName:NSDeviceRGBColorSpace
							 bytesPerRow:(wh * 4)
							bitsPerPixel:32];
	
	pf = [tileRep bitmapData];
	
	int i,j,y,z;
	for (i=0; i<2; i++) {
		for (j=0; j<2; j++) {
			
			oneTile = [tsa objectAtIndex:(j+(i*2))];
			ptr = [[[oneTile representations] lastObject] bitmapData];
			
			for (y=0; y<(8 * sr); y++) {
				for (z=0; z<(8 * 4 * sr); z++) {
					*pf++ = *ptr++;
				}
				pf += (8 * 4 * sr);
			}
		}
		pf = [tileRep bitmapData] + (8 * 4 * sr);
	}
	
	[tileFinal addRepresentation:tileRep];
	return tileFinal;
}

- (NSImage *)tileWithOffset:(int)os palette:(struct sCol *)pal
{
	const char *p1;
	unsigned char *p2;
	
	p1 = [rom bytes] + os;
	
	int sr = mapScale;		// Scale Ratio
	int wh = sr * 8;
	
	NSImage *tileImage = [[NSImage alloc] initWithSize:NSMakeSize(wh,wh)];
	NSBitmapImageRep *tileRep = [[NSBitmapImageRep alloc] 
				initWithBitmapDataPlanes:NULL
							  pixelsWide:wh
							  pixelsHigh:wh
						   bitsPerSample:8
						 samplesPerPixel:4
								hasAlpha:YES
								isPlanar:NO
						  colorSpaceName:NSDeviceRGBColorSpace
							 bytesPerRow:(wh * 4)
							bitsPerPixel:32];
	
	p2 = [tileRep bitmapData];
	
	int i,j,k1,k2;
	unsigned char b1,b2,cp;
	
	for (i=0; i<8; i++) {
		
		b1 = *p1;
		b2 = *(p1 + 8);
		
		for (j=7; j>=0; j--) {
			
			cp = ((b1 >> j) & 0x01) + (((b2 >> j) & 0x01) * 2);		// Bit Weight (0 1 2 3)
			
			for (k1=0; k1<sr*4; k1+=4) {
				for (k2=0; k2<sr*4; k2+=4) {
					
					if (pal[cp].r == 0xEF) {
						*( ( p2 + (wh * k1) ) + k2) = 0;
						*( ( p2 + (wh * k1) ) + k2 + 1) = 0;
						*( ( p2 + (wh * k1) ) + k2 + 2) = 0;
						*( ( p2 + (wh * k1) ) + k2 + 3) = 0;
					} else {
						*( ( p2 + (wh * k1) ) + k2) = pal[cp].r;
						*( ( p2 + (wh * k1) ) + k2 + 1) = pal[cp].g;
						*( ( p2 + (wh * k1) ) + k2 + 2) = pal[cp].b;
						*( ( p2 + (wh * k1) ) + k2 + 3) = 255;
					}
					
				}
			}
			
			p2 += (sr * 4);
			
		}
		
		p2 += sr * (sr - 1) * 8 * 4;
		p1++;
		
	}
	
	[tileImage addRepresentation:tileRep];
	return tileImage;
}

- (NSImage *)emptyTile
{
	int sr = mapScale;		// Scale Ratio
	int wh = sr * 8;
	
	NSImage *tileEmpty = [[NSImage alloc] initWithSize:NSMakeSize(wh,wh)];
	NSBitmapImageRep *tileRep = [[NSBitmapImageRep alloc] 
				initWithBitmapDataPlanes:NULL
							  pixelsWide:wh
							  pixelsHigh:wh
						   bitsPerSample:8
						 samplesPerPixel:4
								hasAlpha:YES
								isPlanar:NO
						  colorSpaceName:NSDeviceRGBColorSpace
							 bytesPerRow:(wh * 4)
							bitsPerPixel:32];
	
	unsigned char *imgPtr = [tileRep bitmapData];
	int i;
	for (i=0; i<256; i++) {
		*imgPtr++ = 0;
	}
	
	[tileEmpty addRepresentation:tileRep];
	return tileEmpty;
}

/* Flip Types

0 = No Flip
1 = Horizontal Flip
2 = Vertical Flip

*/

- (void)flipTile:(NSImage *)tile type:(int)flip
{
	NSBitmapImageRep *tileRep = [[tile representations] lastObject];
	unsigned int *p1 = (unsigned int *)[tileRep bitmapData];
	unsigned int *p2 = p1;
	int sr = mapScale;
	unsigned int dim = 4 * sr;
	unsigned int res;
	
	unsigned int i,j;
	for (i=0; i<(dim * 2); i++) {
		p2 = p1 + (dim * 2) - 1;
		for (j=0; j<dim; j++) {
			res = *p1;
			*p1 = *p2;
			*p2 = res;
			p1++; p2--;
		}
		p1 += 4 * sr;
	}
}

# pragma mark -

- (void)loadAreaData
{
	unsigned int offset;
	unsigned char nb;
	int i,j,k;
	
	unsigned int worldOffset[8] = { 0x4523 , 0x6000 , 0x8523 , 0xA000 , 0 , 0 , 0 , 0 };
	
	for (i=0; i<2; i++) {
		for (j=0; j<0x3F; j++) {
			
			ptr = [rom bytes] + worldOffset[i] + (j * 2) + 0x10;		// Area Pointers Table
			offset = *ptr | *(ptr + 1) << 8;
			ptr = [rom bytes] + offset - 0x8000 + (0x4000 * 1) + 0x10;
			
			nb = *ptr;	//NSLog(@"nb : %X",nb);
			if (nb > 0x80) nb = 0x80;				// 0x80 is an arbitrary limit
			for (k=0; k<nb; k++) {
				areaData[i][j][k] = *ptr++;
			}
		}
	}
	
	for (i=0; i<1; i++) {
		ptr = [rom bytes] + 0x4C3C + 0x10;		// Background Maps
		for (j=1; j<7; j++) {
			nb = *ptr;	//NSLog(@"nb : %X",nb);
			if (nb > 0x80) nb = 0x80;				// 0x80 is an arbitrary limit
			for (k=0; k<nb; k++) {
				bgAreaData[i][j][k] = *ptr++;
			}
		}
	}
}

- (void)loadEnemyData
{
	int i,j,k;
	unsigned char nb;
	unsigned int enemyOffset[8] = { 0x48A0 , 0x48A0 , 0x88A0 , 0x88A0 , 0 , 0 , 0 , 0 };
	
	// Enemy Data //
	
	for (i=0; i<2; i++) {		// Loop for all 8 worlds
		ptr = [rom bytes] + enemyOffset[i] + 0x10;
		for (j=0; j<0x7E; j++) {
			nb = *ptr;	//NSLog(@"nb : %X",nb);
			if (nb > 0x40) nb = 0x40;				// 0x40 is an arbitrary limit
			for (k=0; k<nb; k++) {
				enemyData[i][j][k] = *ptr++;
			}
		}
	}
	
	// Fill the Enemy Sets Matrix //
	
	[matrixEnemies renewRows:0x7E columns:9];
	[matrixEnemies sizeToCells];
	//[matrixEnemies setDrawsBackground:YES];
	//[matrixEnemies setBackgroundColor:[NSColor blackColor]];
	
	for (i=0; i<0x7E; i++) {
		for (j=0; j<9; j++) {
			[[matrixEnemies cellAtRow:i column:j] setBordered:NO];
			if (j == 0) [[matrixEnemies cellAtRow:i column:j] setTitle:[NSString stringWithFormat:@"%02X",i]];
		}
	}
	
	for (i=0; i<0x7E; i++) {
		
		nb = enemyData[cw][i][0];
		if (nb < 3) {
			[[matrixEnemies cellAtRow:i column:1] setImage:[NSImage imageNamed:@"Null.gif"]];
			continue;
		}
		
		if (nb > 16) nb = 16;
		
		for (j=2; j<nb; j+=2) {
			k = enemyData[cw][i][j];
			k &= 0x3F; //NSLog(@"k : %X",k);
			if (k >= [tilesEnemies count]) {
				[[matrixEnemies cellAtRow:i column:j/2] setImage:[NSImage imageNamed:@"Null.gif"]];
				k = [tilesEnemies count] - 1;
			}
			[[matrixEnemies cellAtRow:i column:j/2] setImage:[tilesEnemies objectAtIndex:k]];
		}
	}
}

- (void)loadAssociations
{
	int i,j;
	unsigned int ne,na;
	signed int offset,a1,a2;
	unsigned int areaOffset[8] = { 0x4523 , 0x6000 , 0x8523 , 0xA000 , 0 , 0 , 0 , 0 };
	unsigned int enemyOffset[8] = { 0x45A1 , 0x607E , 0x85A1 , 0xA07E , 0 , 0 , 0 , 0 };
	
	for (i=0; i<2; i++) {	// Loop for all 8 worlds
		
		for (j=0; j<0x3F; j++) {	// 0x3F
			
			ptr = [rom bytes] + areaOffset[i] + (j * 2) + 0x10;		// 0x4523 = Area Pointers for West Hyrule
			a1 = *ptr | *(ptr + 1) << 8;
			
			for (na=0; na<j; na++) {
				ptr = [rom bytes] + areaOffset[i] + (na * 2) + 0x10;
				a2 = *ptr | *(ptr + 1) << 8;
				if (a1 == a2) break;
			}
			
			areaAssoc[i][j] = na; //NSLog(@"Area %02X\t%02X",j,na);
			
			ptr = [rom bytes] + enemyOffset[i] + (j * 2) + 0x10;	// 0x45A1 = Enemy Pointers for West Hyrule
			offset = *ptr | *(ptr + 1) << 8;
			offset -= 0x7000;
			
			ne = 0;
			while (offset > 0) {
				offset -= enemyData[i][ne][0];
				ne++;
				if (ne >= 0x7E) { ne = 0; break; }
			}
			
			enemyAssoc[i][j] = ne; // NSLog(@"Area %02X  %02X  %02X",j,na,ne);
		}
	}
}

# pragma mark -

- (IBAction)changeWorld:(id)sender
{
	int v = [sender indexOfSelectedItem];
	if (v == cw) return;
	
	cw = v;
	ca = 0;
	cd = 0;
	ceg = 0;
	ce = 0;
	NSLog(@"cw : %X",cw);
	
	[self loadArea];
}

- (void)loadArea
{
	int i;
	unsigned char flags;
	unsigned char grass;
	unsigned char bushes;
	unsigned char bit4,bits01;
	unsigned char nc;
	unsigned char cft;
	unsigned char icf;
	
	[self loadTiles];
	
	flags = areaData[cw][cd][1];
	objSet = (flags & 0x80) >> 7;
	areaWidth = ((flags & 0x60) >> 5) + 1;
	grass = 0;
	bushes = 0;
	bit4 = (flags & 0x10) >> 4;
	bits01 = flags & 0x03;
	
	flags = areaData[cw][cd][2];
	nc = (flags & 0x80) >> 7;
	cft = (flags & 0x70) >> 4;
	icf = flags & 0x0F;
	
	NSLog(@"Set %X / Width %X",objSet,areaWidth);
	NSLog(@"Grass %X / Bushes %X",grass,bushes);
	NSLog(@"Bit 4 %X / Bits 01 %X",bit4,bits01);
	NSLog(@"CFT %X / No Ceiling %X",cft,nc);
	NSLog(@"Initial CF %X",icf);
	NSLog(@"----");
	
	[self setBackgroundColor:[NSColor blackColor]];
	[self setDrawsBackground:YES];
	
	nc = enemyAssoc[cw][ca];
	int t = 0;
	for (i=0; i<0x3F; i++) {
		if (enemyAssoc[cw][i] == nc) t++;
	}
	t--; //NSLog(@"t : %d",t);
	
	NSLog(@"co : %X",co);
	
	[self changeObject];
	[self changeEnemy];
	[self redraw];
}

- (IBAction)changeArea:(id)sender
{
	if (sender == nil) ca = 0;
	else ca = [self stringToHex:[sender stringValue]];
	
	if (ca > 0x3E) { NSBeep(); ca = 0x3E; }
	
	ceg = enemyAssoc[cw][ca]; NSLog(@"ceg : %X",ceg);
	cd = areaAssoc[cw][ca];
	[fieldData setStringValue:[self hexToString:cd]];
	[fieldArea setStringValue:[self hexToString:ca]];
	[fieldEnemySet setStringValue:[self hexToString:ceg]];
	[sender selectText:nil];
	
	co = 0;
	ce = 0;
	
	[self changeObject];
	[self changeEnemy];
	[self loadArea];
}

- (IBAction)changeData:(id)sender
{
	unsigned char v = [self stringToHex:[sender stringValue]];
	
	if (v > 0x3E) { NSBeep(); v = 0x3E; }
	areaAssoc[cw][ca] = v;
	cd = v;
	
	[sender setStringValue:[self hexToString:v]];
	[sender selectText:nil];
	
	co = 0;
	
	[self changeObject];
	[self loadArea];
}

- (IBAction)changeEnemySet:(id)sender
{
	unsigned char v = [self stringToHex:[sender stringValue]];
	
	if (v > 0x7E) { NSBeep(); v = 0x7E; }
	enemyAssoc[cw][ca] = v;
	ceg = v;
	ce = 0;
	
	[sender setStringValue:[self hexToString:v]];
	[sender selectText:nil];
	[self loadArea];
}

- (void)changeSize:(int)newSize	// ˆ refaire //
{
	//[self renewRows:13 columns:newSize*16];
	[self renewRows:13 columns:4*16];
	[self sizeToCells];
	//NSLog(@"Size changed to : %X",newSize);
}

- (IBAction)nextObject:(id)sender
{
	unsigned char t = [self totalObjects] - 1;
	
	if (co < t) co++;
	t = [self offsetForObject:co];
	
	[self changeObject];
	[self redraw];
}

- (IBAction)prevObject:(id)sender
{
	if (co > 0) co--;
	
	[self changeObject];
	[self redraw];
}

- (void)changeObject
{
	unsigned char p,s,i;
	unsigned char t = [self offsetForObject:co];
	
	p = areaData[cw][cd][t];
	s = areaData[cw][cd][t+1];
	i = areaData[cw][cd][t+2];
	
	[fieldObjectPosition setStringValue:[self hexToString:p]];
	[fieldObjectType setStringValue:[self hexToString:s]];
	if ((p & 0xF0) < 0xD0 && s == 0x0F) {
		[fieldObjectItem setEnabled:YES];
		[fieldObjectItem setStringValue:[self hexToString:s]];
	} else {
		[fieldObjectItem setEnabled:NO];
		[fieldObjectItem setStringValue:@""];
	}
	
	[self displayObjectName:areaData[cw][cd][t+1] position:areaData[cw][cd][t] world:cw];
}

- (IBAction)changeObjectData:(id)sender		// Tag 0 1 2
{
	int i;
	unsigned char v = [self stringToHex:[sender stringValue]];
	unsigned char offset = 4;
	
	for (i=0; i<co; i++) {
		if (areaData[cw][cd][i+1] == 0x0F && ((areaData[cw][cd][i] & 0xF0) >> 4) < 0x0D) offset++;
		offset += 2;
	}
	
	if ([sender tag] == 0) {		// Position
		areaData[cw][cd][offset] = v;
		if ((v & 0x0F) == 0x0F && (v & 0xF0) < 0xD0) {
			[fieldObjectItem setEnabled:YES];
			[fieldObjectItem setStringValue:@"00"];
		} else {
			[fieldObjectItem setStringValue:@""];
			[fieldObjectItem setEnabled:NO];
		}
	}
	
	if ([sender tag] == 1) {		// Type
		areaData[cw][cd][offset+1] = v;
	}
	
	if ([sender tag] == 2) {		// Item Number
		
	}
	
	[sender setStringValue:[self hexToString:v]];
	[sender selectText:nil];
	[self redraw];
}

- (unsigned char)totalObjects
{
	unsigned char i = 4;
	unsigned char t = 0;
	
	while (i < areaData[cw][cd][0]) {
		if (areaData[cw][cd][i+1] == 0x0F && ((areaData[cw][cd][i] & 0xF0) >> 4) < 0x0D) i++;
		i += 2;
		t++;
	}
	
	return t;
}

- (unsigned char)offsetForObject:(unsigned char)obj
{
	int i;
	unsigned t = 4;
	
	for (i=0; i<obj; i++) {
		if (areaData[cw][cd][i+1] == 0x0F && ((areaData[cw][cd][i] & 0xF0) >> 4) < 0x0D) t++;
		t += 2;
	}
	
	return t;
}

- (IBAction)nextEnemy:(id)sender
{
	unsigned char t = enemyData[cw][ceg][0];
	if (t <= 3) return;
	t /= 2; t--;
	if (ce < t) ce++;
	[self changeEnemy];
	[self redraw];
}

- (IBAction)prevEnemy:(id)sender
{
	unsigned char t = enemyData[cw][ceg][0];
	if (t <= 3) return;
	if (ce > 0) ce--;
	[self changeEnemy];
	[self redraw];
}

- (void)changeEnemy
{
	unsigned char p,s,t;
	
	p = enemyData[cw][ceg][(ce*2)+1];
	s = enemyData[cw][ceg][(ce*2)+2] & 0xC0;
	s >>= 6;
	t = enemyData[cw][ceg][(ce*2)+2] & 0x3F;
	
	if (enemyData[cw][ceg][0] < 2) {
		t = 0xFF;
		[fieldEnemyPosition setStringValue:@""]; [fieldEnemyPosition setEnabled:NO];
		[fieldEnemyScreen setStringValue:@""]; [fieldEnemyScreen setEnabled:NO];
		[fieldEnemyType setStringValue:@""]; [fieldEnemyType setEnabled:NO];
	} else {
		[fieldEnemyPosition setEnabled:YES]; [fieldEnemyPosition setStringValue:[self hexToString:p]];
		[fieldEnemyScreen setEnabled:YES]; [fieldEnemyScreen setStringValue:[self hexToString:s]];
		[fieldEnemyType setEnabled:YES]; [fieldEnemyType setStringValue:[self hexToString:t]];
	}
	[self displayEnemyName:t world:cw];
}

- (IBAction)changeEnemyData:(id)sender		// Tag 0 1 2
{
	unsigned char v = [self stringToHex:[sender stringValue]];
	unsigned char offset = 1 + (ce * 2);
	
	if ([sender tag] == 0) {		// Position
		enemyData[cw][ceg][offset] = v;
	}
	
	if ([sender tag] == 1) {		// Screen
		if (v > 0x03) v = 0x03;
		enemyData[cw][ceg][offset+1] &= 0x3F;
		enemyData[cw][ceg][offset+1] |= (v << 6);
	}
	
	if ([sender tag] == 2) {		// Type
		if (v > 0x3F) v = 0x3F;
		enemyData[cw][ceg][offset+1] &= 0xC0;
		enemyData[cw][ceg][offset+1] |= v;
	}
	
	[sender setStringValue:[self hexToString:v]];
	[sender selectText:nil];
	[self redraw];
}

- (IBAction)changeBushesGrass:(id)sender
{
	// Tag 0 1
}

- (IBAction)changeNoCeiling:(id)sender
{
	
}

- (IBAction)changeObjectSet:(id)sender
{
	objSet = [sender selectedColumn];
	
	[self changeObject];
	[self redraw];
}

# pragma mark -

- (void)displayObjectName:(unsigned char)objectCode position:(unsigned char)positionCode world:(unsigned char)worldCode
{
	NSString *name;
	if (objectCode == 0xFF) [fieldObjects setStringValue:@"No Objects"];
	
	if (worldCode == 0 | worldCode == 1) {
		if ((positionCode & 0xF0) < 0xD0 && objectCode == 0x0F) {	// Collectable Objects
			name = @"Collectable Object";
		} else if ((positionCode & 0xF0) == 0xD0) {
			name = [NSString stringWithFormat:@"New C/F : %X-%X",(1),(0)];
		} else if ((positionCode & 0xF0) == 0xE0) {
			name = [NSString stringWithFormat:@"Skip to Screen %X",(positionCode & 0x0F)];
		} else if ((positionCode & 0xF0) == 0xF0) {
			name = @"Extra Object";
		} else if (objectCode < 0x10) {			// Small Objects
			switch (objectCode) {
				case 0x00: name = @"Gravestone"; break;
				case 0x01: name = @"Cross"; break;
				case 0x02: name = @"Slanted Cross"; break;
				case 0x03: name = @"Tree Stump"; break;
				case 0x04: name = @"Dolmen"; break;
				case 0x05: name = @"Locked Door"; break;
				case 0x06: name = @"Zelda"; break;
				case 0x07: name = @"Zelda"; break;
				case 0x08: name = @"Hole"; break;
				case 0x09: name = @"Long Cloud"; break;
				case 0x0A: name = @"Short Cloud"; break;
				case 0x0B: name = @"Short Cloud"; break;
				case 0x0C: name = @"Short Cloud"; break;
				case 0x0D: name = @"Short Cloud"; break;
				case 0x0E: name = @"Short Cloud"; break;
				case 0x0F: name = @"Short Cloud"; break;
				default: name = @"???";
			}
		} else {
			if (objSet == 0) {
				switch (objectCode & 0xF0) {		// Variable Size Objects - Set 0
					case 0x20: name = @"Forest Leaves, 2 high"; break;
					case 0x30: name = @"Forest Leaves, 2 high"; break;
					case 0x40: name = @"Curtains, 2 high"; break;
					case 0x50: name = @"Forest Leaves, 1 high"; break;
					case 0x60: name = @"Breakable Blocks, 1 high"; break;
					case 0x70: name = @"Horizontal Pit, 1 high"; break;
					case 0x80: name = @"Single Weed, 1 high"; break;
					case 0x90: name = @"Double Weed, 1 high"; break;
					case 0xA0: name = @"North Castle Carpet, 1 high"; break;
					case 0xB0: name = @"North Castle Bricks, 1 high"; break;
					case 0xC0: name = @"Volcano Background, 1 high"; break;
					case 0xD0: name = @"Breakable Blocks, 1 wide"; break;
					case 0xE0: name = @"Tree Trunk, 1 wide"; break;
					case 0xF0: name = @"Column, 1 wide"; break;
					default: name = @"???";
				}
			} else {
				switch (objectCode & 0xF0) {		// Variable Size Objects - Set 1
					case 0x20: name = @"Wide Rock Floor, 2 high"; break;
					case 0x30: name = @"Wide Rock Ceiling, 2 high"; break;
					case 0x40: name = @"Bridge, 2 high"; break;
					case 0x50: name = @"Rock Platform, 1 high"; break;
					case 0x60: name = @"Breakable Blocks, 1 high"; break;
					case 0x70: name = @"Breaking Bridge, 1 high"; break;
					case 0x80: name = @"Single Weed, 1 high"; break;
					case 0x90: name = @"Double Weed, 1 high"; break;
					case 0xA0: name = @"Horizontal Pit, 1 high"; break;
					case 0xB0: name = @"North Castle Bricks, 1 high"; break;
					case 0xC0: name = @"Volcano Background, 1 high"; break;
					case 0xD0: name = @"Breakable Blocks, 1 wide"; break;
					case 0xE0: name = @"Rock Floor, 1 wide"; break;
					case 0xF0: name = @"Vertical Dolmen, 1 wide"; break;
					default: name = @"???";
				}
			}
		}
	}
	
	[fieldObjects setStringValue:[NSString stringWithFormat:@"%02X - %@",co,name]];
}

- (void)displayEnemyName:(unsigned char)enemyCode world:(unsigned char)worldCode
{
	NSString *name;
	
	if (worldCode == 0 | worldCode == 1) {
		switch (enemyCode) {
			case 0x00: name = @"Fairy"; break;
			case 0x01: name = @"Red Jar"; break;
			case 0x02: name = @"Locked Door"; break;
			case 0x03: name = @"Myu"; break;
			case 0x04: name = @"Bot"; break;
			case 0x05: name = @"Bit"; break;
			case 0x06: name = @"Moa"; break;
			case 0x07: name = @"Ache"; break;
			case 0x08: name = @"?"; break;
			case 0x09: name = @"?"; break;
			case 0x0A: name = @"Acheman"; break;
			case 0x0B: name = @"Bubble Gen."; break;
			case 0x0C: name = @"Rock Gen."; break;
			case 0x0D: name = @"Red Deeler"; break;
			case 0x0E: name = @"Blue Deeler"; break;
			case 0x0F: name = @"Bago Bago Gen."; break;
			case 0x10: name = @"Bago Bago"; break;
			case 0x11: name = @"Red Octorok - Jumping"; break;
			case 0x12: name = @"Red Octorok - Moving"; break;
			case 0x13: name = @"Elevator"; break;
			case 0x14: name = @"Orange Moblin"; break;
			case 0x15: name = @"Red Moblin"; break;
			case 0x16: name = @"Blue Moblin"; break;
			case 0x17: name = @"Orange Daira"; break;
			case 0x18: name = @"Red Daira"; break;
			case 0x19: name = @"Orange Goriya"; break;
			case 0x1A: name = @"Red Goriya"; break;
			case 0x1B: name = @"Blue Goriya"; break;
			case 0x1C: name = @"Lowder"; break;
			case 0x1D: name = @"Moby Generator"; break;
			case 0x1E: name = @"Moby"; break;
			case 0x1F: name = @"Megmat"; break;
			case 0x20: name = @"Geldarm"; break;
			case 0x21: name = @"Dumb Moblin Gen."; break;
			case 0x22: name = @"Dumb Moblin"; break;
			default: name = [NSString stringWithFormat:@"Enemy ID %02X",enemyCode];
		}
	}
	
	if (enemyCode == 0xFF) [fieldEnemies setStringValue:@"No Enemies"];
	else [fieldEnemies setStringValue:[NSString stringWithFormat:@"%02X - %@",ce,name]];
}

# pragma mark -

- (void)mouseDown:(NSEvent *)event
{
	int x,y;
	NSPoint mc = [self convertPoint:[[self window] mouseLocationOutsideOfEventStream] fromView:[[self window] contentView]];
	[self getRow:&y column:&x forPoint:mc];
	NSLog(@"Pos : %X,%X",x,y);
}

- (void)redraw
{
	//NSLog(@"Begin Redraw");
	
	int i,j;
	unsigned char b1,b2;
	unsigned char b3 = 0;
	unsigned char bg;
	unsigned char flags;
	
	objx = 0;
	objy = 0;
	
	// Fill Screen with Background Tile //
	
	flags = areaData[cw][cd][2];
	flags &= 0x70;
	flags >>= 4;
	ptr = [rom bytes] + 0x4153 + (flags * 9) + 0x10;
	bg = *(ptr + 0);
	
	for (i=0; i<[self numberOfRows]; i++) {
		for (j=0; j<[self numberOfColumns]; j++) {
			[[self cellAtRow:i column:j] setImage:[tiles objectAtIndex:bg]];
		}
	}
	
	// Ceiling and Floor //
	
	flags = areaData[cw][cd][2];
	[self drawCF:0 code:flags];				// Incorporate the function into this one...
	//NSLog(@"Initial CF %X",flags);
	
	// Should be load behind map here //
	
	// Layers - Bushes and Grass //
	
	flags = areaData[cw][cd][1];
	if (((flags & 0x04) >> 2) == 1) {
		for (i=0; i<0x40; i++) {
			[[self cellAtRow:0x09 column:i] setImage:[tiles objectAtIndex:0x83]];
		}
	}
	
	if (((flags & 0x08) >> 3) == 1) {
		for (i=0; i<0x40; i++) {
			[[self cellAtRow:0x0A column:i] setImage:[tiles objectAtIndex:0x84]];
		}
	}
	
	// Area Data //
	
	objx = 0;
	objy = 0;
	
	for (i=4; i<(areaData[cw][cd][0]); i+=2) {
		
		b1 = areaData[cw][cd][i];
		b2 = areaData[cw][cd][i+1]; //NSLog(@"b1b2 : %X %X",b1,b2);
		
		objy = b1 >> 4;
		
		if (objy == 0xE) {
			objx = (b1 & 0x0F) * 0x10;
			continue;
		}
		
		objx += b1 & 0x0F;
		length = b2 & 0x0F;
		type = b2 >> 4;
		
		if (objy == 0xD) {  // New Default CF
			[self drawCF:objx code:b2];
			//NSLog(@"New Ceiling : %X",(length > 7 ? (length-6) : 1 ));
			//NSLog(@"New Floor   : %X",(length > 7 ? 2 : (length+2) ));
			//NSLog(@"----");
			continue;
		}
		
		if (objy < 0xD) {
			
			if (type == 0) {
				switch (length) {
					case 0x0: [self setSingle:0xC5]; break;
					case 0x1: [self setSingle:0xC6]; break;
					case 0x2: [self setSingle:0xC7]; break;
					case 0x3: [self setVerticalTwo:0x5B]; break;
					case 0x4: [self setOther:4]; break;
					case 0x5: break;	// Locked Door Object
					case 0x6: [self setHorizontalTwo:0x4E]; break;
					case 0x7: [self setHorizontalTwo:0x4E]; break;
					case 0x8: [self setOther:8]; break;
					case 0x9: [self setHorizontalTwo:12]; break;
					case 0xA: [self setHorizontalTwo:13]; break;
					case 0xB: [self setHorizontalTwo:13]; break;
					case 0xC: [self setHorizontalTwo:13]; break;
					case 0xD: [self setHorizontalTwo:13]; break;
					case 0xE: [self setHorizontalTwo:13]; break;
					case 0xF: [self setHorizontalTwo:13]; break;
				}
			}
			
			if (objSet == 0) {
				switch (type) {
					case 0x1: break;	// Crash
					case 0x2: [self setHorizontalDouble:0x04]; break;
					case 0x3: [self setHorizontalDouble:0x06]; break;
					case 0x4: [self setHorizontalDouble:0x08]; break;
					case 0x5: [self setHorizontalSingle:0x0A]; break;
					case 0x6: [self setHorizontalSingle:0x0C]; break;
					case 0x7: [self setHorizontalSingle:0x0E]; break;
					case 0x8: [self setHorizontalSingle:0x10]; break;
					case 0x9: [self setHorizontalSingle:0x12]; break;
					case 0xA: [self setHorizontalSingle:0x14]; break;
					case 0xB: [self setHorizontalSingle:0x16]; break;
					case 0xC: [self setHorizontalSingle:0x18]; break;
					case 0xD: [self setHorizontalSingle:0x1A]; break;
					case 0xE: [self setVerticalSingle:0x1C]; break;
					case 0xF: [self setVerticalSingle:0x1E]; break;
				}
			}
			
			if (objSet == 1) {
				switch (type) {
					case 0x1: break;	// Crash
					case 0x2: [self setHorizontalDouble:0x84]; break;
					case 0x3: [self setHorizontalDouble:0x86]; break;
					case 0x4: [self setHorizontalDouble:0x88]; break;
					case 0x5: [self setHorizontalSingle:0x8A]; break;
					case 0x6: [self setHorizontalSingle:0x8C]; break;
					case 0x7: [self setHorizontalSingle:0x8E]; break;
					case 0x8: [self setHorizontalSingle:0x90]; break;
					case 0x9: [self setHorizontalSingle:0x92]; break;
					case 0xA: [self setHorizontalSingle:0x94]; break;
					case 0xB: [self setHorizontalSingle:0x96]; break;
					case 0xC: [self setVerticalSingle:0x98]; break;
					case 0xD: [self setVerticalSingle:0x9A]; break;
					case 0xE: [self setVerticalSingle:0x9C]; break;
					case 0xF: [self setVerticalSingle:0x9E]; break;
				}
			}
		}
		
		if (objy == 0xF) {
			switch (type) {
				case 0x0: break;	// Crash
				case 0x1: break;	// Crash
				case 0x2: objy = 9; [self setOther:0]; break;
				case 0x3: objy = 9; length = 2; [self setVerticalUp:0x46]; break;
				case 0x4: objy = 9; length = 1; [self setVerticalUp:0x48]; break;
				case 0x5: break;	// Elevator
			}
		}
		
		if ((objy < 13) & (b2 == 0xF)) {
			b3 = areaData[cw][cd][i+2];
			NSLog(@"Collectable Object (%02X)",b3);		// Switch collectable objects here...
			//[self setItem:b3];
			i++;
		}
		
		//NSLog(@"----");
	}
	
	/*
	for (i=0; i<0x40; i++) {
		[[self cellAtRow:0 column:i] setImage:[tiles objectAtIndex:i]];
		[[self cellAtRow:1 column:i] setImage:[tiles objectAtIndex:i+0x40]];
		[[self cellAtRow:2 column:i] setImage:[tiles objectAtIndex:i+0x80]];
		[[self cellAtRow:3 column:i] setImage:[tiles objectAtIndex:i+0xC0]];
	}
	*/
	
	[self setNeedsDisplay:YES];
	//NSLog(@"----");
}

- (void)redrawAreaData
{
	
}

- (void)drawCF:(int)x code:(unsigned char)h
{
	int i,j;
	int c = 0;
	int f = 0;
	int cf = (h & 0x0F);
	int nc = (h >> 7);
	unsigned char areaType;
	unsigned char c1,c2,f1,f2,bt;
	
	if (cf < 8) {
		c = 2;
		f = cf+2;
	}
	
	if ((cf >= 8) && (cf < 15)) {
		c = cf-6;
		f = 2;
	}
	
	// Should use tileLayers array //
	
	areaType = areaData[cw][cd][2];
	areaType &= 0x70;
	areaType >>= 4;
	ptr = [rom bytes] + 0x4153 + (areaType * 9) + 0x10;
	c1 = *(ptr + 7);
	c2 = *(ptr + 6);
	f1 = *(ptr + 3);
	f2 = *(ptr + 7);
	bt = *(ptr + 8);
	
	for (j=x; j<[self numberOfColumns]; j++) {
		
		for (i=0; i<[self numberOfRows]; i++) {
			[[self cellAtRow:i column:j] setImage:[tiles objectAtIndex:0]];
		}
		
		for (i=0; i<c; i++) {
			if ((i+1) < c) [[self cellAtRow:i column:j] setImage:[tiles objectAtIndex:c1]];
			else [[self cellAtRow:i column:j] setImage:[tiles objectAtIndex:c2]];
		}
		
		for (i=0; i<f; i++) {
			if ((i+1) == f) [[self cellAtRow:12-i column:j] setImage:[tiles objectAtIndex:f1]];
			else [[self cellAtRow:12-i column:j] setImage:[tiles objectAtIndex:f2]];
		}
		
		if (nc == 1) {
			for (i=0; i<2; i++) {
				[[self cellAtRow:i column:j] setImage:[tiles objectAtIndex:0]];
			}
		}
	}
	
	if (cf == 0xF) {		// Fill with wall...
		for (i=0; i<[self numberOfRows]; i++) {
			for (j=0; j<[self numberOfColumns]; j++) {
				[[self cellAtRow:i column:j] setImage:[tiles objectAtIndex:c1]];
			}
		}
	}
	
	// Bottom Row //
	
	for (i=0; i<0x40; i++) {
		if ([tiles indexOfObject:[[self cellAtRow:0xC column:i] image]] == 0x89) continue;
		[[self cellAtRow:0xC column:i] setImage:[tiles objectAtIndex:bt]];
	}
}

- (void)drawRect:(NSRect)theRect
{
	[super drawRect:[self frame]];
	
	// Draw Enemies
	
	NSImage *enemyToDraw;
	NSPoint loc;
	
	unsigned char b1,b2,n;
	unsigned short x,y;
	
	int i;
	for (i=1; i<(enemyData[cw][ceg][0]); i+=2) {
		
		b1 = enemyData[cw][ceg][i];
		b2 = enemyData[cw][ceg][i+1];
		
		x = (b1 & 0x0F) | ((b2 >> 2) & 0x30);   // For Extra Certainty...
		y = (b1 >> 4) & 0x0F;
		
		//NSLog(@"b1b2 : %X %X",b1,b2);
		//NSLog(@"xy : %X %X",x,y);
		
		loc.x = x * 0x10;
		
		if (y == 0) y = 1;
		else y += 2;
		loc.y = y * 0x10;
		
		n = b2 & 0x3F;
		enemyToDraw = [tilesEnemies objectAtIndex:n];
		
		loc.y += [enemyToDraw size].height;
		
		[enemyToDraw compositeToPoint:loc operation:NSCompositeSourceOver];
	}
	
	// Draw Screen Separator Lines //
	
	NSBezierPath *lines = [NSBezierPath bezierPath];
	for (i=0; i<4; i++) {
		[lines moveToPoint:NSMakePoint( i * 0x100 , 0 )];
		[lines lineToPoint:NSMakePoint( i * 0x100 , [self frame].size.height )];
	}
	
	[[NSColor colorWithDeviceWhite:1.0 alpha:0.8] set];
	[lines stroke];
	
	//
	
	/* Draw Transparent Sheet Overlay
	
	NSRect sheetRect = [self frame];
	sheetRect.size.width = (256 * mapScale * 3);
	//[[NSColor colorWithDeviceRed:1.0 green:0.2 blue:0.2 alpha:0.2] set];
	[[NSColor colorWithDeviceWhite:0 alpha:0.6] set];
	
	NSBezierPath *sheet = [NSBezierPath bezierPathWithRect:sheetRect];
	[sheet fill];
	[[NSColor whiteColor] set];
	[sheet stroke];
	
	*/
}

# pragma mark -

- (void)setSingle:(int)code
{
	[[self cellAtRow:objy column:objx] setImage:[tiles objectAtIndex:code]];
}

- (void)setHorizontalTwo:(int)code
{
	[[self cellAtRow:objy column:objx] setImage:[tiles objectAtIndex:code]];
	[[self cellAtRow:objy column:objx+1] setImage:[tiles objectAtIndex:code+1]];
}

- (void)setVerticalTwo:(int)code
{
	[[self cellAtRow:objy column:objx] setImage:[tiles objectAtIndex:code]];
	[[self cellAtRow:objy+1 column:objx] setImage:[tiles objectAtIndex:code+1]];
}

- (void)setHorizontalSingle:(int)code
{
	int i;
	unsigned char t = tileCodes[cw][(code >> 7)][(code & 0x7F)];
	for (i=0; i<=length; i++) {
		[[self cellAtRow:objy column:objx+i] setImage:[tiles objectAtIndex:t]];
	}
}

- (void)setHorizontalDouble:(int)code
{
	int i;
	unsigned char t1 = tileCodes[cw][(code >> 7)][(code & 0x7F)];
	unsigned char t2 = tileCodes[cw][(code >> 7)][(code & 0x7F)+1];
	
	for (i=0; i<=length; i++) {
		[[self cellAtRow:objy column:objx+i] setImage:[tiles objectAtIndex:t1]];
		[[self cellAtRow:objy+1 column:objx+i] setImage:[tiles objectAtIndex:t2]];
	}
}

- (void)setVerticalUp:(int)code
{
	int i;
	unsigned char t1 = tileCodes[cw][(code >> 7)][(code & 0x7F)];
	unsigned char t2 = tileCodes[cw][(code >> 7)][(code & 0x7F)+1];
	
	for (i=0; i<length; i++) {
		[[self cellAtRow:objy-i column:objx] setImage:[tiles objectAtIndex:t1]];
	}
	[[self cellAtRow:objy-i column:objx] setImage:[tiles objectAtIndex:t2]];
}

- (void)setVerticalSingle:(int)code
{
	int i;
	unsigned char t1 = tileCodes[cw][(code >> 7)][(code & 0x7F)];
	unsigned char t2 = tileCodes[cw][(code >> 7)][(code & 0x7F)+1];
	
	[[self cellAtRow:objy column:objx] setImage:[tiles objectAtIndex:t1]];
	for (i=1; i<=length; i++) {
		[[self cellAtRow:objy+i column:objx] setImage:[tiles objectAtIndex:t2]];
	}
}

- (void)setVerticalDouble:(int)code
{
	int i = 0;
	[[self cellAtRow:objy+i column:objx] setImage:[tiles objectAtIndex:code]];
	[[self cellAtRow:objy+i column:objx+1] setImage:[tiles objectAtIndex:code]];
	for (i=1; i<=length; i++) {
		[[self cellAtRow:objy+i column:objx] setImage:[tiles objectAtIndex:code+1]];
		[[self cellAtRow:objy+i column:objx+1] setImage:[tiles objectAtIndex:code+1]];
	}
}

- (void)setOther:(int)code
{
	int i;
	unsigned char dol[4] = { 0x56 , 0x57 , 0x57 , 0x58 };
	
	switch (code) {
	case 0:		// Lava Pit
		for (i=0; i<=length; i++) {
			[[self cellAtRow:objy+1 column:objx+i] setImage:[tiles objectAtIndex:0x80]];
			[[self cellAtRow:objy+2 column:objx+i] setImage:[tiles objectAtIndex:0x89]];
			[[self cellAtRow:objy+3 column:objx+i] setImage:[tiles objectAtIndex:0x89]];
		}
		break;
	case 4:		// Dolmen
		[[self cellAtRow:objy+1 column:objx+0] setImage:[tiles objectAtIndex:0x59]];
		[[self cellAtRow:objy+2 column:objx+0] setImage:[tiles objectAtIndex:0x5A]];
		[[self cellAtRow:objy+1 column:objx+3] setImage:[tiles objectAtIndex:0x59]];
		[[self cellAtRow:objy+2 column:objx+3] setImage:[tiles objectAtIndex:0x5A]];
		for (i=0; i<4; i++) {
			[[self cellAtRow:objy column:objx+i] setImage:[tiles objectAtIndex:dol[i]]];
		}
		break;
	case 8:		// Pit
		while (objy < 13) {
			[[self cellAtRow:objy column:objx] setImage:[tiles objectAtIndex:0]];	// Should be background tile
			objy++;
		}
		break;
	case 1:
		// Objects Other are different according to Area type (Overworld, Palaces, Towns)
		break;
	}
}

- (void)setItem:(int)code
{
	[[self cellAtRow:objy column:objx] setImage:[tilesItems objectAtIndex:code]];
}

# pragma mark -

- (unsigned int)stringToHex:(NSString *)str
{
	unsigned int res = 0;
	unsigned short temp;
	unsigned char move = 0x00000000;	// Super Zero !
	
	int i;
	for (i=[str length]-1; i>=0; i--) {
		temp = [[str uppercaseString] characterAtIndex:i];
		if ((temp >= 'A') && (temp <= 'F')) temp -= 0x37;
		else temp -= 0x30;
		res |= temp <<= move;
		move += 4;
	}
	
	return res;
}

- (NSString *)hexToString:(unsigned int)hex
{
	NSMutableString *res = [NSMutableString stringWithCapacity:4];
	[res setString:[NSString stringWithFormat:@"%02X",hex]];
	
	/*unsigned char pad = [res length] + ([res length] & 1);
	
	while ([res length] < pad) {
		[res insertString:@"0" atIndex:0];
	}*/
	
	return res;
}

# pragma mark -

- (void)dealloc
{
	if (tiles) [tiles release];
	if (tilesItems) [tilesItems release];
	[super dealloc];
}

@end

