#import "BWSideView.h"
#import "BWOverWorld.h"
#import "BWGameData.h"
#import "BWMainObject.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];
	tilesBosses = [[NSMutableArray alloc] initWithCapacity:8];
	tilesLink = [[NSMutableArray alloc] initWithCapacity:2];
	tilesNumbers = [[NSMutableArray alloc] initWithCapacity:0x7E];
	tilesEditor = [[NSMutableArray alloc] initWithCapacity:0x100];
	tiles8x8 = [[NSMutableArray alloc] initWithCapacity:0x900];			// 0x100 * 8 palettes + No Candle palette
	tiles32x32 = [[NSMutableArray alloc] initWithCapacity:0x900];		// 0x100 * 8 palettes + No Candle palette
	
	mapScale = 1;
	[self setRefusesFirstResponder:YES];
	//[scrollView setLineScroll:(mapScale * 16)];
	[self setCellSize:NSMakeSize( (16 * mapScale) , (16 * mapScale) )];
	[self sizeToCells];
}

- (void)open:(NSMutableData *)theRom
{
	rom = theRom;
	
	/* Code for Pointer Output //
	int i;
	unsigned int a1,a2;
	ptr = [rom bytes] + 0x15565 + 0x10;
	
	for (i=0; i<0x24; i++) {
		a1 = *ptr | *(ptr + 1) << 8;
		//a2 = a1 - 0x7000 + 0x108A0;
		a2 = a1 - 0x8000 + 0x14000;
		printf("%02X%02X -> %04X\n",*ptr,*(ptr+1),a2);
		ptr += 2;
	}
	
	return;
	*/
	
	cw = 0;
	
	teo = 1;
	tep = 0;
	teTSA = -1;
	
	[matrixTiles renewRows:5 columns:0x41];
	[matrixTiles sizeToCells];
	
	tileCodesArrowBasePosition = [buttonTileCodesArrow frame];
	
	[self loadPalettes];
	[self loadTileMappings];
	[self loadTileCodes];
	[self loadTileLayers];
	[self loadAreaData];
	[self loadEnemyAttributes];
	
	[self loadTiles];
	[self loadTilesEnemies];
	[self loadTilesBosses];
	[self loadTilesItems];
	//[self loadTilesLink];
	[self loadNumbers];
	//[self loadTilesEditor];
	[self loadEnemyData];
	[self loadAssociations];
	[self loadConnectivity];
	[self loadDoorDestinations];
	[self loadItemBits];
	
	[self renewRows:0x0D columns:0x40];
	[self sizeToCells];
	
	//[objGameData loadEnemies];
	
	[self changeWorld:nil];
	[self changeTileObject:nil];
	
	/* Test Menu Image View //
	
	NSImage *img = [[NSImage alloc] initWithContentsOfFile:@"/Users/sim/Desktop/b.png"];
	//NSLog(@"%f %f",[img size].width,[img size].height);
	NSImageView *imgView = [[NSImageView alloc] initWithFrame:NSMakeRect(0,0,[img size].width,[img size].height)];
	[imgView setImage:img];
	[menuItemTest setView:imgView];
	[img release];
	[imgView release];
	
	// Test Menu Slider //
	
	NSView *view = [[NSView alloc] initWithFrame:NSMakeRect(0,0,116,21)];
	NSSlider *slider = [[NSSlider alloc] initWithFrame:NSMakeRect(20,0,96,21)];
	[view addSubview:slider];
	[menuItemTest setView:view];
	[slider release];
	[view release];
	
	*/
}

# pragma mark -

- (void)loadPalettes
{
	int i,j;
	unsigned int palOffset[8] = { 0x400E , 0x400E , 0x800E , 0x800E , 0xC00E , 0x1000E , 0x1000E , 0x1400E };
	
	// Less than 8 groups, because some regions share the same data
	
	for (i=0; i<8; 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 char bank;
	unsigned int tmOffset[8] = { 0x4500 , 0x4500 , 0x8500 , 0x8500 , 0xC500 , 0x10500 , 0x10500 , 0x14500 };
	
	// Less than 8 groups, because some regions share the same data
		
	for (i=0; i<8; i++) {
		
		if (i == 0 || i == 1) bank = 1;
		if (i == 2 || i == 3) bank = 2;
		if (i == 4) bank = 3;
		if (i == 5 || i == 6) bank = 4;
		if (i == 7) bank = 5;
		
		for (j=0; j<4; j++) {
			
			ptr = [rom bytes] + tmOffset[i] + (j * 2) + 0x10;
			offset = *ptr | *(ptr + 1) << 8;
			offset -= 0x8000;
			offset += 0x4000 * bank;
			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*0x40)+k]);
			}
		}
	}
}

- (void)loadTileCodes
{
	int i,j;
	
	// These addresses could be generated from the values in the code, to account for possible expansion and/or data displacement
	
	unsigned int tcOffset0[8] = { 0x41B7 , 0x41B7 , 0x81B7 , 0x81B7 , 0x41B7 , 0x1017B , 0x1017B , 0x1418F };
	unsigned int tcOffset1[8] = { 0x41D5 , 0x41D5 , 0x81D5 , 0x81D5 , 0x41D5 , 0x10199 , 0x10199 , 0x141AD };
	
	// Towns don't have any Tile Code tables (set to arbitrary address)
	// Palaces Type A/B and Great Palace have Object Set 0 only (Object Set 1 yields garbage)
	
	// Less than 8 groups, because some regions share the same data
	
	for (i=0; i<8; i++) {
		ptr = [rom bytes] + tcOffset0[i] + 0x10;		// Set 0
		for (j=2; j<0x20; j++) {
			tileCodes[i][0][j] = *ptr++;
		}
		
		ptr = [rom bytes] + tcOffset1[i] + 0x10;		// Set 1
		for (j=2; j<0x20; j++) {
			tileCodes[i][1][j] = *ptr++;
		}
	}
}

- (void)loadTileLayers
{
	int i,j,k;
	
	// These pointers could be constructed from the values in the table nearby and the arbitrary value in the nearby code
	
	unsigned int offsets[5] = { 0x4153 , 0x8153 , 0xC61F , 0x10153 , 0x14153 };
	
	// Less than 8 groups, because some regions share the same data
	
	for (i=0; i<5; i++) {
		ptr = [rom bytes] + offsets[i] + 0x10;
		for (j=0; j<8; j++) {						// Arbitrary maximum
			for (k=0; k<9; k++) {
				tileLayers[i][j][k] = *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];
	
	unsigned char bank;
	if (cw == 0 || cw == 1) bank = 1;
	if (cw == 2 || cw == 3) bank = 2;
	if (cw == 4) bank = 3;
	if (cw == 5 || cw == 6) bank = 4;
	if (cw == 7) bank = 5;
	
	unsigned char graphicsBank;
	
	ptr = [rom bytes] + 0x1CD2A + 0x10;
	ptr += regionCode * 4;
	ptr += palaceCode;
	graphicsBank = *ptr;
	
	if (cw == 0 || cw == 1) graphicsBank = 1;	// West Hyrule
	if (cw == 2 || cw == 3) graphicsBank = 2;	// East Hyrule
	if (cw == 4) graphicsBank = 3;				// Towns
	
	int i,j,x;
	unsigned char palOffset;
	for (i=0; i<4; i++) {
		
		for (j=0; j<0x100; j+=4) {
			
			palCode = (areaData[cw][cd][3] & 0x38) >> 3;
			//palCode = 8;	// test for No Candle option (if NoCandle, palCode = 8)
			
			if (cw == 5 || cw == 6) {					// Palaces Type A/B
				ptr = [rom bytes] + 0x1CD35 + 0x10;
				ptr += (regionCode * 4) + palaceCode;
				palOffset = *ptr;
				
				if (palCode == 0) ptr = [rom bytes] + 0x10470 + 0x10;
				else ptr = [rom bytes] + 0x13F00 + 0x10;
				
				// Test for Hidden Palettes in Palaces
				ptr = [rom bytes] + 0x1000E + 0x10;
				palOffset = palCode * 0x10;
				//
				
				ptr += palOffset;
				ptr += (i * 4);
				
				for (x=0; x<4; x++) {
					if ((*ptr) == 0xFF) tp[x] = nesPal[0x40];
					else tp[x] = nesPal[(*ptr)];
					ptr++;
				}
			} else {									// Outside Areas, Towns and Great Palace
				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 * graphicsBank) + 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 = 0x16;
	int offset;
	unsigned char palCode;
	struct sCol tp[4];
	
	unsigned char bank;
	if (cw == 0 || cw == 1) bank = 1;
	if (cw == 2 || cw == 3) bank = 2;
	if (cw == 4) bank = 3;
	if (cw == 5 || cw == 6) bank = 4;
	if (cw == 7) bank = 5;
	
	// TO DO : Experience Bags with Points Overlays (50 100 200 500)
	
	int i,x;
	for (i=0; i<max; i++) {
		
		palCode = 1;
		if (i == 0x10) palCode = 3;		// Exception for Blue Jar as Object
		if (i == 0x11) palCode = 2;		// Exception for Red Jar as Object
		if (i == 0x12) palCode = 0;		// Exception for Doll
		
		palCode *= 4;
		palCode += 0x90;	// check if it can be 0xA0 or another value
		
		for (x=0; x<4; x++) {
			tp[x] = nesPal[palettes[cw][palCode + x]];
			tp[0] = nesPal[0x40];
		}
		
		for (x=0; x<4; x++) {
			offset = 0x20010 + (0x2000 * bank) + (tmi[(i*4)+x] * 0x10);
			if (i == 0x13) offset += 0x2000;
			
			if (tmi[(i*4)+x] == 0) [tsa addObject:[self emptyTile]];
			else [tsa addObject:[self tileWithOffset:offset palette:tp]];
			
			// Conditions here...
			
			if ((i == 0x0E) && (x >= 2)) [self flipTile:[tsa lastObject] type:1];	// Magic Container
			if ((i == 0x0F) && (x >= 2)) [self flipTile:[tsa lastObject] type:1];	// Heart Container
			if ((i == 0x13) && (x >= 2)) [self flipTile:[tsa lastObject] type:1];	// Kidnapped Child
			if ((i == 0x14) && (x >= 2)) [self flipTile:[tsa lastObject] type:1];	// Trophy
			if ((i == 0x15) && (x >= 2)) [self flipTile:[tsa lastObject] type:1];	// Medicine
		}
		
		tileFinal = [self tileBlockWithTiles:tsa];
		[tilesItems addObject:tileFinal];
		[tsa removeAllObjects];
	}
}

- (void)loadEnemyAttributes
{
	int i,j,k;
	unsigned int ea[5] = { 0x54D5 , 0x94D5 , 0xD4D5 , 0x114D5 , 0x154D5 };
	
	// Less than 8 groups, because some regions share the same data
	
	for (i=0; i<5; i++) {
		ptr = [rom bytes] + ea[i] + 0x10;
		for (j=0; j<4; j++) {
			for (k=0; k<0x24; k++) {
				enemyAttributes[i][j][k] = *ptr++;
			}
		}
	}
}

- (void)loadTilesEnemies
{
	[tilesEnemies removeAllObjects];
	
	NSMutableArray *tlist = [NSMutableArray arrayWithCapacity:16];
	NSImage *spriteImage;
	
	int offset;
	unsigned char spriteSize;
	unsigned char palCode;
	struct sCol tp[4];
	
	unsigned char bank;
	if (cw == 0 || cw == 1) bank = 0;	// West Hyrule / Death Mountain
	if (cw == 2 || cw == 3) bank = 1;	// East Hyrule / Maze Island
	if (cw == 4) bank = 2;				// Towns
	if (cw == 5 || cw == 6) bank = 3;	// Palaces Type A/B
	if (cw == 7) bank = 4;				// Great Palace
	
	unsigned char graphicsBank;
	
	ptr = [rom bytes] + 0x1CD2A + 0x10;
	ptr += regionCode * 4;
	ptr += palaceCode;
	graphicsBank = *ptr;
	
	if (cw == 0 || cw == 1) graphicsBank = 1;	// West Hyrule
	if (cw == 2 || cw == 3) graphicsBank = 2;	// East Hyrule
	if (cw == 4) graphicsBank = 3;				// Towns
	
	unsigned char tileMappingsBank = bank;
	if (cw == 6) tileMappingsBank = 4;
	if (cw == 7) tileMappingsBank = 5;
	
	int i,j,x;
	for (i=0; i<0x24; i++) {
		
		if (cw < 4 && i == 0x0B) { [tilesEnemies addObject:[NSImage imageNamed:@"BubbleGenerator.png"]]; continue; }
		if (cw < 4 && i == 0x0C) { [tilesEnemies addObject:[NSImage imageNamed:@"RockGenerator.png"]]; continue; }
		if (cw == 4 && (i == 0x0C || i == 0x21 || i == 0x22)) { [tilesEnemies addObject:[NSImage imageNamed:@"Dialog.png"]]; continue; }
		if (cw == 4 && i == 0x23) { [tilesEnemies addObject:[NSImage imageNamed:@"TownfolksGenerator.png"]]; continue; }
		// Falling Blocks Generator
		// Tinsuit Generator
		// Crystal Slot
		// Energy Ball Shooter (Right/Left)
		
		[tlist removeAllObjects];
		
		palCode = ((enemyAttributes[bank][0][i]) & 0xC0) >> 6; //NSLog(@"%02X : %X",j,palCode);
		if (cw == 4) palCode = (enemyAttributes[bank][1][i]) & 0x03;
		
		if (i <= 0x02) palCode = 1;						// Fairy, Red Jar, Locked Door (Enemy Attributes don't have the correct palette)
		if (i == 0x0F) palCode = 2;						// Bago Bago Generator (same palette as Bago)
		
		if (bank == 2) {
			if (i == 0x03) palCode = 3;		// Ache
			if (i == 0x04) palCode = 3;		// Bot
			if (i == 0x05) palCode = 2;		// Bit
			if (i == 0x07) palCode = 2;		// Acheman
			if (i == 0x0A) palCode = 2;		// Ache (talker)
			if (i == 0x0B) palCode = 3;		// Bit (talker)
			if (i == 0x0F) palCode = 1;		// Wise Man
		}
		
		if (cw < 4) {
			if (i == 0x1D) palCode = 1;					// Moby Generator (same palette as Moby)
			if (i == 0x21) palCode = 1;					// Dumb Moblin Generator (same palette as Dumb Moblin)
			if (i == 0x01) palCode = 2;					// Exception for Red Jar
			if (bank == 1 && i == 0x1D) palCode = 2;	// Exception for Rock Tossing Lizalfos
		}
		
		if (bank == 3 && i == 0x06) palCode = 3;		// Slow Bubble (to differentiate from Fast Bubble)
		if (bank == 3 && i == 0x0B) palCode = 1;		// Tinsuit Generator (not necessary if arbitrary icon is used)
		if (bank == 3 && i == 0x14) palCode = 3;		// Crystal Slot (not necessary if arbitrary icon is used)
		if (bank == 3 && i == 0x15) palCode = 3;		// Crystal
		
		if (cw == 6 && i == 0x0F) palCode = 3;			// Ra (instead of Bago Bago Generator)
		if (cw == 6 && i == 0x10) palCode = 2;			// Flame (instead of Bago Bago)
		if (cw == 6 && i == 0x1D) palCode = 2;			// Wizard (instead of Mago)
		if (cw == 6 && i == 0x1E) palCode = 3;			// Doomknocker (instead of Guma)
		
		if (cw == 7 && i == 0x15) palCode = 2;			// Fast Bubble (to differentiate from Slow Bubble)
		if (cw == 7 && i == 0x17) palCode = 2;			// Exception for Big Bubble
		
		palCode *= 4;
		
		if (cw < 4) {
			// 0xA0 for Forest and Caves, 0x90 for everything else
			if (((areaData[cw][cd][2] & 0x70) >> 4) > 1) palCode += 0x90;
			else palCode += 0xA0;
		} else if (cw == 4) {
			palCode += 0x90;
		} else if (cw > 4) {
			palCode += 0x90;
		}
		
		for (x=0; x<4; x++) {
			tp[x] = nesPal[palettes[cw][palCode + x]];
			tp[0] = nesPal[0x40];
		}
		
		if (i == 0x13 && bank != 2) {	// Elevator (except in Towns)
			for (j=0; j<18; j++) {
				if (j > 2 && j < 15) [tlist addObject:[self emptyTile]];
				else {
					offset = 0x20000 + (0x2000 * graphicsBank) + (0xAC * 0x10) + 0x10;
					[tlist addObject:[self tileWithOffset:offset palette:tp]];
				}
			}
			spriteImage = [self graphicWithTiles:tlist type:0x36];
			[tilesEnemies addObject:spriteImage];
			continue;
		}
		
		spriteSize = tme[tileMappingsBank][i][0];
		if (spriteSize == 0xFF) {
			[tilesEnemies addObject:[NSImage imageNamed:@"Undefined.png"]];
			continue;
		}
		
		for (j=1; j<17; j++) {
			if (tme[tileMappingsBank][i][j] == 0x00) break;
			if (tme[tileMappingsBank][i][j] == 0x01) { [tlist addObject:[self emptyTile]]; continue; }
			
			offset = 0x20000 + (0x2000 * graphicsBank) + (tme[tileMappingsBank][i][j] * 0x10) + 0x10;
			[tlist addObject:[self tileWithOffset:offset palette:tp]];
			
			if (bank < 2 && i == 0x03 && j == 4) [self flipTile:[tlist lastObject] type:1];					// Myu
			if (bank < 2 && i == 0x04 && (j == 2 || j == 4)) [self flipTile:[tlist lastObject] type:1];		// Bot
			
			if (bank < 2 && i == 0x05 && (j == 2 || j == 4)) [self flipTile:[tlist lastObject] type:1];		// Bit
			if (bank < 2 && i == 0x07 && (j == 2 || j == 4)) [self flipTile:[tlist lastObject] type:1];		// Ache
			if (bank < 2 && i == 0x0A && (j == 2 || j == 4)) [self flipTile:[tlist lastObject] type:1];		// Acheman
			
			if (bank == 1 && i == 0x16 && (j == 2 || j == 4)) [self flipTile:[tlist lastObject] type:1];	// Leever
			
			if (bank == 2) {
				if ((i >= 0x03 && i <= 0x05) && (j == 2 || j == 4)) [self flipTile:[tlist lastObject] type:1];
				if (i == 0x07 && (j == 2 || j == 4)) [self flipTile:[tlist lastObject] type:1];
				if ((i == 0x0A || i == 0x0B) && (j == 2 || j == 4)) [self flipTile:[tlist lastObject] type:1];
			}
			
			if (bank == 3 && i == 0x03 && j == 4) [self flipTile:[tlist lastObject] type:1];				// Myu
			if (bank == 3 && i == 0x04 && (j == 2 || j == 4)) [self flipTile:[tlist lastObject] type:1];	// Bot
			if (bank == 3 && i == 0x06 && (j == 2 || j == 4)) [self flipTile:[tlist lastObject] type:1];	// Bubble - Slow (Palaces Type A/B)
			if (bank == 3 && i == 0x0E && (j == 2 || j == 4)) [self flipTile:[tlist lastObject] type:1];	// Bubble - Fast (Palaces Type A/B)
			
			if (bank == 4) {
				if (i == 0x03 && j == 4) [self flipTile:[tlist lastObject] type:1];					// Myu
				if (i == 0x04 && (j == 2 || j == 4)) [self flipTile:[tlist lastObject] type:1];		// Bot
				if (i == 0x07 && (j == 2 || j == 4)) [self flipTile:[tlist lastObject] type:1];		// Ache
				if (i == 0x0A && (j == 2 || j == 4)) [self flipTile:[tlist lastObject] type:1];		// Acheman
				if (i == 0x14 && (j == 2 || j == 4)) [self flipTile:[tlist lastObject] type:1];		// Bubble - Slow (Great Palace)
				if (i == 0x15 && (j == 2 || j == 4)) [self flipTile:[tlist lastObject] type:1];		// Bubble - Fast (Great Palace)
				if (i == 0x17 && (((j - 1) % 4) > 1)) [self flipTile:[tlist lastObject] type:1]; 	// Big Bubble (Great Palace)
			}
		}
		
		spriteImage = [self graphicWithTiles:tlist type:spriteSize];
		
		// Add overlays to generator graphics (0F 1D 21)
		// And moving Octorok
		// Something for invisible enemies (needs Cross)
		
		[tilesEnemies addObject:spriteImage];
	}
	
	// Enemy Matrix //
	
	for (i=0; i<0x24; i++) {
		[[matrixEnemies cellAtRow:(i / 8) column:(i % 8)] setImage:[tilesEnemies objectAtIndex:i]];
		[[matrixEnemies cellAtRow:(i / 8) column:(i % 8)] setTag:i];
	}
	
	/*NSString *path;
	NSData *ddd;
	for (i=0; i<[tilesEnemies count]; i++) {
		path = [[NSString stringWithFormat:@"~/Desktop/TestTiles/enemy%02d.tiff",i] stringByExpandingTildeInPath];
		ddd = [[tilesEnemies objectAtIndex:i] TIFFRepresentationUsingCompression:NSTIFFCompressionLZW factor:1.0];
		[ddd writeToFile:path atomically:YES];
	}*/
}

- (void)loadTilesBosses
{
	[tilesBosses removeAllObjects];
	
	NSMutableArray *tlist = [NSMutableArray arrayWithCapacity:24];
	NSImage *spriteImage;
	NSMutableArray *bossParts = [NSMutableArray arrayWithCapacity:10];
	
	int offset;
	unsigned char spriteSize;
	unsigned char palCode;
	struct sCol tp[4];
	
	unsigned char graphicsBank;
	ptr = [rom bytes] + 0x1CD2A + 0x10;
	ptr += regionCode * 4;
	ptr += palaceCode;
	graphicsBank = *ptr;
	
	unsigned char palBosses[10] = { 2 , 3 , 3 , 3 , 2 , 2 , 1 , 1 , 3 , 0 };
	
	int i,j,x;
	for (i=0; i<10; i++) {
		
		// make exception for Dark Link Trigger Battle ?
		
		[tlist removeAllObjects];
		
		palCode = (palBosses[i] * 4) + 0x90;
		for (x=0; x<4; x++) {
			tp[x] = nesPal[palettes[cw][palCode + x]];
			tp[0] = nesPal[0x40];
		}
		
		spriteSize = tmboss[i][0];
		
		for (j=1; j<61; j++) {
			if (tmboss[i][j] == 0x00) break;
			if (tmboss[i][j] == 0x01) { [tlist addObject:[self emptyTile]]; continue; }
			
			offset = 0x20000 + (0x2000 * graphicsBank) + (tmboss[i][j] * 0x10) + 0x10;
			[tlist addObject:[self tileWithOffset:offset palette:tp]];
			
			if (i == 8 && ((j - 1) % 6) > 2) [self flipTile:[tlist lastObject] type:1];		// Thunderbird flipped tiles
		}
		
		spriteImage = [self graphicWithTiles:tlist type:spriteSize];
		
		[bossParts addObject:spriteImage];
	}
	
	[tilesBosses addObject:[bossParts objectAtIndex:0]];	// Horsehead
	
	// Helmethead
	NSImage *helmethead = [[NSImage alloc] initWithSize:NSMakeSize(16,48)];
	[helmethead lockFocus];
	[[tilesEnemies objectAtIndex:0x22] compositeToPoint:NSMakePoint(4,27) operation:NSCompositeSourceOver];
	[[bossParts objectAtIndex:1] compositeToPoint:NSZeroPoint operation:NSCompositeSourceOver];
	[helmethead unlockFocus];
	[tilesBosses addObject:helmethead];
	[helmethead release];
	
	// Rebonack
	NSImage *rebonack = [bossParts objectAtIndex:2];
	[rebonack lockFocus];
	[[bossParts objectAtIndex:3] compositeToPoint:NSMakePoint(16,16) operation:NSCompositeSourceOver];
	[rebonack unlockFocus];
	[tilesBosses addObject:rebonack];
	
	[tilesBosses addObject:[bossParts objectAtIndex:4]];	// Carock
	[tilesBosses addObject:[bossParts objectAtIndex:5]];	// Gooma
	
	// Barba
	NSImage *barba = [[NSImage alloc] initWithSize:NSMakeSize(38,128)];
	[barba lockFocus];
	[[bossParts objectAtIndex:6] compositeToPoint:NSMakePoint(0,112) operation:NSCompositeSourceOver];
	[[bossParts objectAtIndex:7] compositeToPoint:NSMakePoint(18,96) operation:NSCompositeSourceOver];
	[[bossParts objectAtIndex:7] compositeToPoint:NSMakePoint(20,80) operation:NSCompositeSourceOver];
	[[bossParts objectAtIndex:7] compositeToPoint:NSMakePoint(22,64) operation:NSCompositeSourceOver];
	[[bossParts objectAtIndex:7] compositeToPoint:NSMakePoint(20,48) operation:NSCompositeSourceOver];
	[[bossParts objectAtIndex:7] compositeToPoint:NSMakePoint(18,32) operation:NSCompositeSourceOver];
	[[bossParts objectAtIndex:7] compositeToPoint:NSMakePoint(16,16) operation:NSCompositeSourceOver];
	[[bossParts objectAtIndex:7] compositeToPoint:NSMakePoint(14,0) operation:NSCompositeSourceOver];
	[barba unlockFocus];
	[tilesBosses addObject:barba];
	[barba release];
	
	[tilesBosses addObject:[bossParts objectAtIndex:8]];	// Thunderbird
	[tilesBosses addObject:[bossParts objectAtIndex:9]];	// Dark Link
	
	/*NSString *path;
	NSData *ddd;
	for (i=0; i<[tilesEnemies count]; i++) {
		path = [[NSString stringWithFormat:@"~/Desktop/TestTiles/enemy%02d.tiff",i] stringByExpandingTildeInPath];
		ddd = [[tilesEnemies objectAtIndex:i] TIFFRepresentationUsingCompression:NSTIFFCompressionLZW factor:1.0];
		[ddd writeToFile:path atomically:YES];
	}*/
}

- (void)loadTilesLink
{
	int i,x;
	int offset;
	
	[tilesLink removeAllObjects];
	
	NSMutableArray *tlist = [NSMutableArray arrayWithCapacity:4];
	//NSImage *final;
	
	struct sCol tp[4];
	tp[0] = nesPal[0x40];
	for (x=1; x<4; x++) {
		tp[x] = nesPal[palettes[cw][0xA0 + x]];		// Grass or Forest ? (A0 90)
	}
	
	unsigned char tm[16] = { 0 , 2 , 1 , 3 , 0x26 , 6 , 0x27 , 7 ,
							2 , 0 , 3 , 1 , 6 , 0x26 , 7 , 0x27 };
	
	offset = 0x20000 + 0x10;
	
	for (i=0; i<8; i++) {
		[tlist addObject:[self tileWithOffset:(offset + (tm[i] * 0x10)) palette:tp]];
	}
	[tilesLink addObject:[self graphicWithTiles:tlist type:0x24]];
	
	[tlist removeAllObjects];
	
	for (i=0; i<8; i++) {
		[tlist addObject:[self tileWithOffset:(offset + (tm[i+8] * 0x10)) palette:tp]];
		[self flipTile:[tlist lastObject] type:1];
	}
	[tilesLink addObject:[self graphicWithTiles:tlist type:0x24]];
}

- (void)loadNumbers
{
	int i,x;
	int offset;
	
	[tilesNumbers removeAllObjects];
	
	struct sCol tp[4];
	tp[0] = nesPal[0x40];
	for (x=1; x<4; x++) {
		tp[x] = nesPal[palettes[cw][0 + x]];	// Palette Code 0
	}
	
	NSImage *digit1;
	NSImage *digit2;
	NSImage *finalNumber;
	
	offset = 0x20000 + 0x1000 + 0xD00 + 0x10;
	
	for (i=0; i<0x100; i++) {
		digit1 = [self tileWithOffset:(offset + ((i / 0x10) * 0x10)) palette:tp];
		digit2 = [self tileWithOffset:(offset + ((i % 0x10) * 0x10)) palette:tp];
		finalNumber = [[NSImage alloc] initWithSize:NSMakeSize(16,8)];
		[finalNumber lockFocus];
		//[[NSColor blackColor] set];
		//NSRectFill(NSMakeRect(0,0,16,8));
		[digit1 drawAtPoint:NSMakePoint(0,0) fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0];
		[digit2 drawAtPoint:NSMakePoint(8,0) fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0];
		[finalNumber unlockFocus];
		[tilesNumbers addObject:finalNumber];
		[finalNumber release];
	}
}

- (void)loadTilesEditor
{
	//[objMain setScale:2];
	mapScale = 2;
	
	[tilesEditor removeAllObjects];
	
	NSImage *tileFinal;
	NSMutableArray *tsa = [NSMutableArray arrayWithCapacity:4];
	
	unsigned int offset;
	unsigned char palCode;
	unsigned char tm;
	struct sCol tp[4];
	
	unsigned char bank;
	if (cw == 0 || cw == 1) bank = 1;
	if (cw == 2 || cw == 3) bank = 2;
	if (cw == 4) bank = 3;
	if (cw == 5 || cw == 6) bank = 4;
	if (cw == 7) bank = 5;
	
	unsigned char graphicsBank;
	
	ptr = [rom bytes] + 0x1CD2A + 0x10;
	ptr += regionCode * 4;
	ptr += palaceCode;
	graphicsBank = *ptr;
	
	if (cw == 0 || cw == 1) graphicsBank = 1;	// West Hyrule
	if (cw == 2 || cw == 3) graphicsBank = 2;	// East Hyrule
	if (cw == 4) graphicsBank = 3;				// Towns
	
	int i,j,x;
	unsigned char palOffset;
	for (i=0; i<4; i++) {
		
		for (j=0; j<0x100; j+=4) {
			
			palCode = (areaData[cw][cd][3] & 0x38) >> 3;
			//palCode = 8;	// test for No Candle option (if NoCandle, palCode = 8)
			
			if (cw == 5 || cw == 6) {					// Palaces Type A/B
				ptr = [rom bytes] + 0x1CD35 + 0x10;
				ptr += (regionCode * 4) + palaceCode;
				palOffset = *ptr;
				
				if (palCode == 0) ptr = [rom bytes] + 0x10470 + 0x10;
				else ptr = [rom bytes] + 0x13F00 + 0x10;
				
				// Test for Hidden Palettes in Palaces
				ptr = [rom bytes] + 0x1000E + 0x10;
				palOffset = palCode * 0x10;
				//
				
				ptr += palOffset;
				ptr += (i * 4);
				
				for (x=0; x<4; x++) {
					if ((*ptr) == 0xFF) tp[x] = nesPal[0x40];
					else tp[x] = nesPal[(*ptr)];
					ptr++;
				}
			} else {									// Outside Areas, Towns and Great Palace
				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]];
					//if (i == 0 && j == 0) NSLog(@"%02X",palettes[cw][palCode + x]);
				}
			}
			
			for (x=0; x<4; x++) {
				tm = tileMappings[cw][(i*0x100)+j+x];
				offset = 0x20000 + (0x2000 * graphicsBank) + 0x1000 + (tm * 0x10) + 0x10;
				[tsa addObject:[self tileWithOffset:offset palette:tp]];
			}
			
			tileFinal = [self tileBlockWithTiles:tsa];
			[tilesEditor addObject:tileFinal];
			[tsa removeAllObjects];
		}
	}
	
	unsigned char t;
	
	[[matrixTiles cellAtRow:0 column:0] setBordered:NO];
	[[matrixTiles cellAtRow:0 column:0] setBackgroundColor:[NSColor blackColor]];
	
	for (i=1; i<0x41; i++) {
		[[matrixTiles cellAtRow:0 column:i] setImage:[tilesNumbers objectAtIndex:(i-1)]];
		[[matrixTiles cellAtRow:0 column:i] setBordered:NO];
		[[matrixTiles cellAtRow:0 column:i] setBackgroundColor:[NSColor blackColor]];
	}
	
	for (i=0; i<4; i++) {
		[[matrixTiles cellAtRow:(i+1) column:0] setImage:[tilesNumbers objectAtIndex:(i * 0x40)]];
		[[matrixTiles cellAtRow:(i+1) column:0] setBordered:NO];
		[[matrixTiles cellAtRow:(i+1) column:0] setBackgroundColor:[NSColor blackColor]];
	}
	
	for (i=0; i<4; i++) {
		for (j=0; j<0x40; j++) {
			[[matrixTiles cellAtRow:(i+1) column:(j+1)] setImage:[tiles objectAtIndex:(i * 0x40) + j]];
			[[matrixTiles cellAtRow:(i+1) column:(j+1)] setBordered:NO];
			[[matrixTiles cellAtRow:(i+1) column:(j+1)] setBackgroundColor:[NSColor blackColor]];
		}
	}
	
	for (i=1; i<0x10; i++) {
		t = tileCodes[cw][objSet][(i * 2)];
		[[matrixTileCodes cellAtRow:(i-1) column:0] setImage:[tilesEditor objectAtIndex:t]];
		[[matrixTileCodes cellAtRow:(i-1) column:0] setBordered:NO];
		t = tileCodes[cw][objSet][(i * 2) + 1];
		[[matrixTileCodes cellAtRow:(i-1) column:1] setImage:[tilesEditor objectAtIndex:t]];
		[[matrixTileCodes cellAtRow:(i-1) column:1] setBordered:NO];
	}
	
	for (i=0; i<4; i++) {
		[[matrixVerticalSingle cellAtRow:i column:0] setBordered:NO];
		[[matrixVerticalTwo cellAtRow:i column:0] setBordered:NO];
		[[matrixHorizontalSingle cellAtRow:0 column:i] setBordered:NO];
		[[matrixHorizontalDouble cellAtRow:0 column:i] setBordered:NO];
		[[matrixHorizontalDouble cellAtRow:1 column:i] setBordered:NO];
	}
	
	// Load 8x8 tiles //
	
	[tiles8x8 removeAllObjects];
	
	[objMain setScale:2];
	
	for (i=0; i<9; i++) {
		
		palCode = i * 0x10;
		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 (j=0; j<0x100; j++) {
			offset = 0x20000 + (0x2000 * graphicsBank) + 0x1000 + (j * 0x10) + 0x10;
			tileFinal = [objMain tileWithOffset:offset palette:tp];
			[tiles8x8 addObject:tileFinal];
		}
	}
	
	for (i=0; i<0x100; i++) {
		[[matrixAllTiles cellAtRow:(i / 0x10) column:(i % 0x10)] setImage:[tiles8x8 objectAtIndex:i]];
		[[matrixAllTiles cellAtRow:(i / 0x10) column:(i % 0x10)] setBordered:NO];
	}
	
	// Load 8x8 tiles with 4x scale //
	
	[tiles32x32 removeAllObjects];
	
	[objMain setScale:4];
	
	for (i=0; i<(9 * 4 * 4); i++) {
		
		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 (j=0; j<0x100; j++) {
			offset = 0x20000 + (0x2000 * graphicsBank) + 0x1000 + (j * 0x10) + 0x10;
			tileFinal = [objMain tileWithOffset:offset palette:tp];
			[tiles32x32 addObject:tileFinal];
		}
	}
	
	// Set Palettes Matrix //
	
	int r,c;
	for (i=0; i<9; i++) {
		r = (i / 2) * 5;
		c = (i % 2) * 5;
		palCode = i * 0x10;
		for (j=0; j<4; j++) {
			[[matrixEditorPalettes cellAtRow:(r+j) column:(c+0)] setBackgroundColor:[objMain colorForCode:palettes[cw][palCode + (j*4) + 0]]];
			[[matrixEditorPalettes cellAtRow:(r+j) column:(c+1)] setBackgroundColor:[objMain colorForCode:palettes[cw][palCode + (j*4) + 1]]];
			[[matrixEditorPalettes cellAtRow:(r+j) column:(c+2)] setBackgroundColor:[objMain colorForCode:palettes[cw][palCode + (j*4) + 2]]];
			[[matrixEditorPalettes cellAtRow:(r+j) column:(c+3)] setBackgroundColor:[objMain colorForCode:palettes[cw][palCode + (j*4) + 3]]];
		}
	}
	
	for (i=0; i<24; i++) {
		for (j=0; j<9; j++) {
			[[matrixEditorPalettes cellAtRow:i column:j] setBordered:NO];
		}
	}
	
	////
	
	for (i=0; i<16; i++) {
		[[matrixTiles01 cellAtRow:(i / 8) column:(i % 8)] setBordered:NO];
	}
	
	mapScale = 1;
}

/* 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 (Zora)
22 = 2 Blocks High, 2 Blocks Wide (Aru Lowder)
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 *)graphicWithTiles:(NSArray *)tilesList type:(unsigned char)size
{
	int h = size & 0x0F;
	int w = (size & 0xF0) >> 4;
	
	if (size == 0) { h = 0x10; w = 0x10; }
	
	int width = w * 8 * mapScale;
	int height = h * 8 * mapScale;
	
	NSImage *tileGraphic = [[[NSImage alloc] initWithSize:NSMakeSize(width,height)] autorelease];
	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<h; i++) {
		for (j=0; j<w; j++) {
			imageToDraw = [tilesList objectAtIndex:j+(i*w)];
			pt.y = (h - i - 1) * [imageToDraw size].height;
			pt.x = j * [imageToDraw size].width;
			[imageToDraw drawAtPoint:pt fromRect:NSZeroRect operation:1 fraction:1.0];
		}
	}
	[tileGraphic unlockFocus];
	
	return tileGraphic;
}

- (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
{
	int i,j,k;
	unsigned int offset;
	unsigned char nb;
	unsigned char bank;
	
	unsigned int worldOffset[8] = { 0x4523 , 0x6000 , 0x8523 , 0xA000 , 0xC523 , 0x10523 , 0x12000 , 0x14523 };
	
	for (i=0; i<8; i++) {
		
		if (i == 0 || i == 1) bank = 1;
		if (i == 2 || i == 3) bank = 2;
		if (i == 4) bank = 3;
		if (i == 5 || i == 6) bank = 4;
		if (i == 7) bank = 5;
		
		for (j=0; j<0x3F; j++) {
			
			ptr = [rom bytes] + worldOffset[i] + (j * 2) + 0x10;		// Area Pointers Table
			offset = *ptr | *(ptr + 1) << 8;
			if (offset < 0x8000 || offset > 0xFFFF) continue;
			ptr = [rom bytes] + offset - 0x8000 + (0x4000 * bank) + 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++;
			}
		}
	}
	
	// Background Maps
	
	unsigned int bgWorldOffset[5] = { 0x4000 , 0x8000 , 0xC000 , 0x10000 , 0x14000 };
	
	for (i=0; i<5; i++) {
		for (j=1; j<7; j++) {
			
			bgAreaData[i][j][0] = 0x01;	// if Area address is invalid, this will generate nothing
			
			ptr = [rom bytes] + bgWorldOffset[i] + ((j - 1) * 2) + 0x10;
			offset = *ptr | *(ptr + 1) << 8;
			if (offset < 0x8000 || offset > 0xFFFF) continue;
			ptr = [rom bytes] + offset - 0x8000 + (0x4000 * (i + 1)) + 0x10;
			
			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
{
	// Enemy Data must be the same for Worlds that use the same data set
	
	int i,j,k;
	unsigned char nb;
	unsigned int enemyOffset[8] = { 0x48A0 , 0x48A0 , 0x88A0 , 0x88A0 , 0xC8A0 , 0x108A0 , 0x108A0 , 0x148A0 };
	
	// Enemy Data //
	
	for (i=0; i<8; 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 //
	
	[matrixEnemySets renewRows:0x7E columns:9];
	[matrixEnemySets sizeToCells];
	//[matrixEnemySets setDrawsBackground:YES];
	//[matrixEnemySets setBackgroundColor:[NSColor blackColor]];
	
	/*
	NSColor *color = [NSColor whiteColor];
	NSFont *font = [NSFont fontWithName:@"Monaco" size:14];
	NSMutableParagraphStyle *paraStyle = [[NSMutableParagraphStyle alloc] init];
	[paraStyle setAlignment:NSCenterTextAlignment];
	NSDictionary *attr = [NSDictionary dictionaryWithObjectsAndKeys:font, NSFontAttributeName,
			color, NSForegroundColorAttributeName,
			paraStyle, NSParagraphStyleAttributeName, nil];
	NSMutableAttributedString *str = [[NSMutableAttributedString alloc] initWithString:@"00" attributes:attr];
	*/
	for (i=0; i<0x7E; i++) {
		for (j=0; j<9; j++) {
			[[matrixEnemySets cellAtRow:i column:j] setBordered:NO];
			//if (j == 0) [[matrixEnemySets cellAtRow:i column:j] setTitle:[NSString stringWithFormat:@"%02X",i]];
			//if (j == 0) {
			//	[str replaceCharactersInRange:NSMakeRange(0,2) withString:[NSString stringWithFormat:@"%02X",i]];
			//	[[matrixEnemySets cellAtRow:i column:0] setAttributedTitle:str];
			//	[[matrixEnemySets cellAtRow:i column:0] setSelectable:NO];
			//}
		}
	}
	
	for (i=0; i<0x24; i++) {
		[[matrixEnemies cellAtRow:(i / 8) column:(i % 8)] setBordered:NO];
		[[matrixEnemies cellAtRow:(i / 8) column:(i % 8)] setBackgroundColor:[NSColor blackColor]];
	}
	for (i=0x24; i<0x28; i++) {
		[[matrixEnemies cellAtRow:(i / 8) column:(i % 8)] setBordered:NO];
		[[matrixEnemies cellAtRow:(i / 8) column:(i % 8)] setEnabled:NO];
	}
}

- (void)loadAssociations
{
	int i,j;
	unsigned int ne,na;
	signed int offset,a1,a2;
	unsigned int areaOffset[8] =  { 0x4523 , 0x6000 , 0x8523 , 0xA000 , 0xC523 , 0x10523 , 0x12000 , 0x14523 };
	unsigned int enemyOffset[8] = { 0x45A1 , 0x607E , 0x85A1 , 0xA07E , 0xC5A1 , 0x105A1 , 0x1207E , 0x145A1 };
	
	// Enemy Data in WH/EH is shared
	// Enemy Data in Palaces Type A/B is shared
	
	for (i=0; i<8; i++) {
		for (j=0; j<0x3F; j++) {
			enemyAssoc[i][j] = 0;
		}
	}
	
	for (i=0; i<8; i++) {	// Loop for all 8 worlds
		
		for (j=0; j<0x3F; j++) {	// 0x3F
			
			ptr = [rom bytes] + areaOffset[i] + (j * 2) + 0x10;
			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;
			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);
		}
	}
}

- (void)loadConnectivity
{
	int i,j;
	unsigned int connectivityOffsets[8] = { 0x471B , 0x61F8 , 0x871B , 0xA1F8 , 0xC71B , 0x1071B , 0x121F8 , 0x1471B };
	
	for (i=0; i<8; i++) {
		ptr = [rom bytes] + connectivityOffsets[i] + 0x10;
		for (j=0; j<0x40; j++) {
			connectivity[i][j][0] = *ptr++;
			connectivity[i][j][1] = *ptr++;
			connectivity[i][j][2] = *ptr++;
			connectivity[i][j][3] = *ptr++;
		}
	}
}

- (void)loadDoorDestinations
{
	int i;
	
	ptr = [rom bytes] + 0xC817 + 0x10;
	for (i=0; i<0x22; i++) {					// Original limit is 1C * 4 = 70, but table is followed by unused bytes, real limit is 89 bytes
		doorDestinations[i][0] = *ptr++;
		doorDestinations[i][1] = *ptr++;
		doorDestinations[i][2] = *ptr++;
		doorDestinations[i][3] = *ptr++;
	}
}

- (void)loadItemBits
{
	int i,j;
	ptr = [rom bytes] + 0x17B15 + 0x10;
	for (i=0; i<7; i++) {
		for (j=0; j<0x20; j++) {
			itemBits[i][j] = *ptr++;
		}
	}
}

# pragma mark -

- (IBAction)changeWorld:(id)sender
{
	int v;
	if (sender == nil) {
		v = 0;
		[fieldRegionCode setStringValue:[self hexToString:0]];
		[fieldPalaceCode setStringValue:[self hexToString:0]];
	} else {
		v = [sender indexOfSelectedItem];
		if (v == cw) return;
	}
	
	cw = v;
	ca = 0;
	cd = 0;
	ceg = 0;
	ce = 0;
	fromDoor = 0xFF;
	
	if (cw < 4) {
		[tabOptions setHidden:YES];
	} else {
		[tabOptions setHidden:NO];
		if (cw == 4) [tabOptions selectFirstTabViewItem:nil];
		else [tabOptions selectLastTabViewItem:nil];
	}
	
	// Test Matrix Palettes
	
	int i,j;
	for (i=0; i<[matrixPalettes numberOfRows]; i++) {
		for (j=0; j<[matrixPalettes numberOfColumns]; j++) {
			[[matrixPalettes cellAtRow:i column:j] setBordered:NO];
		}
	}
	
	int n = 0;
	for (i=0; i<9; i++) {
		for (j=0; j<0x10; j++) {
			[[matrixPalettes cellAtRow:(j/4)+((i/2)*5) column:(j%4)+((i%2)*5)] setBackgroundColor:[objMain colorForCode:palettes[cw][n++]]];
		}
	}
	
	[self loadTilesEnemies];
	[self loadTilesBosses];
	
	// Enemy Sets //
	
	unsigned char nb;
	int k;
	
	for (i=0; i<0x7E; i++) {
		
		for (j=0; j<8; j++) {
			[[matrixEnemySets cellAtRow:i column:j] setImage:nil];
		}
		
		nb = enemyData[cw][i][0];
		if (nb < 3) {
			[[matrixEnemySets cellAtRow:i column:1] setImage:[NSImage imageNamed:@"Undefined.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]) {
				[[matrixEnemySets cellAtRow:i column:j/2] setImage:[NSImage imageNamed:@"Undefined.gif"]];
				k = [tilesEnemies count] - 1;
			} else {
				[[matrixEnemySets cellAtRow:i column:j/2] setImage:[tilesEnemies objectAtIndex:k]];
			}
		}
	}
	
	for (i=0; i<0x7E; i++) {
		[[matrixEnemySets cellAtRow:i column:0] setImage:[tilesNumbers objectAtIndex:i]];
	}
	
	////
	
	[self changeArea:nil];
}

- (void)loadArea
{
	int i;
	unsigned char flags;
	unsigned char nc;
	unsigned char extra;
	// unsigned char bits01;
	unsigned char ts;
	unsigned char icf;
	unsigned char pal;
	unsigned char bg;
	
	[self loadTiles];
	
	[fieldDestinationLeft setStringValue:[self hexToString:(connectivity[cw][ca][0] >> 2)]];
	[fieldDestinationDown setStringValue:[self hexToString:(connectivity[cw][ca][1] >> 2)]];
	[fieldDestinationUp setStringValue:[self hexToString:(connectivity[cw][ca][2] >> 2)]];
	[fieldDestinationRight setStringValue:[self hexToString:(connectivity[cw][ca][3] >> 2)]];
	[fieldEnterCodeLeft setStringValue:[self hexToString:(connectivity[cw][ca][0] & 0x03)]];
	[fieldEnterCodeDown setStringValue:[self hexToString:(connectivity[cw][ca][1] & 0x03)]];
	[fieldEnterCodeUp setStringValue:[self hexToString:(connectivity[cw][ca][2] & 0x03)]];
	[fieldEnterCodeRight setStringValue:[self hexToString:(connectivity[cw][ca][3] & 0x03)]];
	
	flags = areaData[cw][cd][1];
	objSet = flags >> 7;
	areaWidth = (flags & 0x60) >> 5;
	extra = (flags & 0x1C) >> 2;
	// bits01 = flags & 0x03;
	
	flags = areaData[cw][cd][2];
	nc = (flags & 0x80) >> 7;		// No Ceiling (0-1)
	ts = (flags & 0x70) >> 4;		// Tile Set for Ceiling/Floor (0-7)
	icf = flags & 0x0F;				// Initial Ceiling/Floor (0-F)
	
	flags = areaData[cw][cd][3];
	pal = (flags & 0x38) >> 3;		// Palette Group (0-7)
	bg = flags & 0x07;				// Background Map Code (0-7)
	
	enterCode = [objOverWorld enterCodeForArea:ca world:cw];
	if (fromDoor != 0xFF) {
		enterCode = fromDoor; // If it's a Door Destination in a Town ("Go" Button Pressed)
		fromDoor = 0xFF;
	}
	
	//NSLog(@"Set %X / Width %X",objSet,areaWidth);
	//NSLog(@"Bits 01 %X",bits01);
	//NSLog(@"CFT %X / No Ceiling %X",cft,nc);
	//NSLog(@"Initial CF %X",icf);
	//NSLog(@"----");
	
	[checkNoCeiling setState:nc];
	[fieldExtraLayer setStringValue:[self hexToString:extra]];
	
	[fieldInitialCF setStringValue:[self hexToString:icf]];
	[fieldAreaWidth setStringValue:[self hexToString:areaWidth]];
	[fieldEnterCode setStringValue:[self hexToString:enterCode]];
	
	[fieldTileSet setStringValue:[self hexToString:ts]];
	[fieldPalette setStringValue:[self hexToString:pal]];
	[fieldBGMap setStringValue:[self hexToString:bg]];
	
	[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);
	
	//[self changeTileObject:nil];
	
	[self loadTilesEditor];
	
	[self changeObject];
	[self changeEnemy];
	[self redraw];
}

- (IBAction)changeArea:(id)sender
{
	int i;
	
	if (sender == nil) ca = 0;
	else ca = [self stringToHex:[fieldArea stringValue]];
	
	if ([sender tag] == 1 && ca > 0) ca -= 1;
	if ([sender tag] == 2 && ca < 0x3E) ca += 1;
	
	if (ca > 0x3E) { NSBeep(); ca = 0x3E; }
	
	//NSLog(@"Change Area : %02X",ca);
	
	ceg = enemyAssoc[cw][ca];
	cd = areaAssoc[cw][ca];
	
	[fieldArea setStringValue:[self hexToString:ca]];
	[fieldData setStringValue:[self hexToString:cd]];
	[fieldEnemySet setStringValue:[self hexToString:ceg]];
	[fieldArea selectText:nil];
	
	unsigned char d,e;
	if (cw == 4) {
		for (i=0; i<4; i++) {
			d = (doorDestinations[ca][i] & 0xFC) >> 2;
			e = doorDestinations[ca][i] & 0x03;
			[[matrixDoorDestinations cellAtRow:0 column:i] setStringValue:[self hexToString:d]];
			[[matrixDoorDestinations cellAtRow:1 column:i] setStringValue:[self hexToString:e]];
		}
	}
	
	co = 0;
	ce = 0;
	
	//[self changeObject];
	//[self changeEnemy];
	[self loadArea];
}

- (void)changeArea:(unsigned char)areaCode world:(unsigned char)w enter:(unsigned char)enterCode
{
	// must be changed to account for World Code AND Region Code
	worldCode = w;
	[menuWorlds selectItemAtIndex:worldCode];	// World Codes are not strictly 0-7
	[self changeWorld:menuWorlds];
	[fieldArea setStringValue:[self hexToString:areaCode]];
	[self changeArea:fieldArea];
	
	[[self window] makeKeyAndOrderFront:nil];
}

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

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

# pragma mark -

- (IBAction)nextObject:(id)sender
{
	unsigned char t = [self totalObjects] - 1;
	//unsigned char offset = [self offsetForObject:co];
	
	if (co < t) 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:i]];
	} else {
		[fieldObjectItem setEnabled:NO];
		[fieldObjectItem setStringValue:@""];
	}
	
	NSString *name = [self getObjectName:areaData[cw][cd][t+1] position:areaData[cw][cd][t] item:i world:cw];
	[fieldObjects setStringValue:[NSString stringWithFormat:@"%02X - %@",co,name]];
}

/*- (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
		
	}
	
	[self displayObjectName:areaData[cw][cd][offset+1] position:areaData[cw][cd][offset] item:areaData[cw][cd][offset]+2 world:cw];
	
	[sender setStringValue:[self hexToString:v]];
	[sender selectText:nil];
	
	[self redraw];
}*/

- (IBAction)changeObjectPosition:(id)sender		// Tag 00 + 01 02 03 04
{
	int i;
	unsigned char t = [sender tag];
	unsigned char v = [self stringToHex:[fieldObjectPosition stringValue]];
	unsigned char offset = [self offsetForObject:co];
	unsigned char nb = areaData[cw][cd][0];
	
	BOOL oldDataIsItem;
	BOOL newDataIsItem;
	
	oldDataIsItem = ((areaData[cw][cd][offset] & 0xF0) < 0xD0 && areaData[cw][cd][offset+1] == 0x0F);
	
	if (t == 1 && (v & 0x0F) > 0) v -= 1;			// Left Arrow
	if (t == 2 && (v & 0x0F) < 0x0F) v += 1;		// Right Arrow
	if (t == 3 && (v & 0xF0) > 0) v -= 0x10;		// Up Arrow
	if (t == 4 && (v & 0xF0) < 0xF0) v += 0x10;		// Down Arrow
	areaData[cw][cd][offset] = v;
	
	newDataIsItem = ((areaData[cw][cd][offset] & 0xF0) < 0xD0 && areaData[cw][cd][offset+1] == 0x0F);
	
	if (oldDataIsItem && !newDataIsItem) {
		for (i=(offset+2); i<nb; i++) {
			areaData[cw][cd][i] = areaData[cw][cd][i+1];
		}
		areaData[cw][cd][0] -= 1;
		[fieldObjectItem setEnabled:NO];
		[fieldObjectItem setStringValue:@""];
		//NSLog(@"Not Item Anymore");
		//[self outputAreaData];
	}
	
	if (!oldDataIsItem && newDataIsItem) {
		for (i=nb; i>(offset+2); i--) {
			areaData[cw][cd][i] = areaData[cw][cd][i-1];
		}
		areaData[cw][cd][offset+2] = 0;
		areaData[cw][cd][0] += 1;
		[fieldObjectItem setEnabled:YES];
		[fieldObjectItem setStringValue:[self hexToString:0]];
		//NSLog(@"Is Item Now");
		//[self outputAreaData];
	}
	
	[fieldObjectPosition setStringValue:[self hexToString:v]];
	[fieldObjectPosition selectText:nil];
	
	NSString *name = [self getObjectName:areaData[cw][cd][offset+1] position:areaData[cw][cd][offset] item:areaData[cw][cd][offset+2] world:cw];
	[fieldObjects setStringValue:[NSString stringWithFormat:@"%02X - %@",co,name]];
	
	[self redraw];
}

- (void)outputAreaData
{
	int i;
	for (i=0; i<areaData[cw][cd][0]; i++) {
		printf("%02X",areaData[cw][cd][i]);
		if (i == 3) printf("\n");
		else if (((i-3) % 0x10) == 0) printf("\n");
		else printf(" ");
	}
}

- (IBAction)changeObjectType:(id)sender
{
	int i;
	unsigned char t = [sender tag];
	unsigned char v = [self stringToHex:[fieldObjectType stringValue]];
	unsigned char offset = [self offsetForObject:co];
	unsigned char nb = areaData[cw][cd][0];
	
	BOOL oldDataIsItem;
	BOOL newDataIsItem;
	
	oldDataIsItem = ((areaData[cw][cd][offset] & 0xF0) < 0xD0 && areaData[cw][cd][offset+1] == 0x0F);
	
	if (t == 1 && (v & 0x0F) > 0) v -= 1;			// Left Arrow
	if (t == 2 && (v & 0x0F) < 0x0F) v += 1;		// Right Arrow
	if (t == 3 && (v & 0xF0) < 0xF0) v += 0x10;		// Up Arrow
	if (t == 4 && (v & 0xF0) > 0) v -= 0x10;		// Down Arrow
	areaData[cw][cd][offset+1] = v;
	
	newDataIsItem = ((areaData[cw][cd][offset] & 0xF0) < 0xD0 && areaData[cw][cd][offset+1] == 0x0F);
	
	if (oldDataIsItem && !newDataIsItem) {
		for (i=(offset+2); i<nb; i++) {
			areaData[cw][cd][i] = areaData[cw][cd][i+1];
		}
		areaData[cw][cd][0] -= 1;
		[fieldObjectItem setEnabled:NO];
		[fieldObjectItem setStringValue:@""];
		//NSLog(@"Not Item Anymore");
		//[self outputAreaData];
	}
	
	if (!oldDataIsItem && newDataIsItem) {
		for (i=nb; i>(offset+2); i--) {
			areaData[cw][cd][i] = areaData[cw][cd][i-1];
		}
		areaData[cw][cd][offset+2] = 0;
		areaData[cw][cd][0] += 1;
		[fieldObjectItem setEnabled:YES];
		[fieldObjectItem setStringValue:[self hexToString:0]];
		//NSLog(@"Is Item Now");
		//[self outputAreaData];
	}
	
	[fieldObjectType setStringValue:[self hexToString:v]];
	[fieldObjectType selectText:nil];
	
	NSString *name = [self getObjectName:areaData[cw][cd][offset+1] position:areaData[cw][cd][offset] item:areaData[cw][cd][offset+2] world:cw];
	[fieldObjects setStringValue:[NSString stringWithFormat:@"%02X - %@",co,name]];
	
	[self redraw];
}

- (IBAction)changeObjectItem:(id)sender
{
	unsigned char t = [sender tag];
	unsigned char v = [self stringToHex:[fieldObjectItem stringValue]];
	unsigned char offset = [self offsetForObject:co];
	
	if (t == 1 && v < 0xFF) v += 1;		// Up Arrow
	if (t == 2 && v > 0) v -= 1;		// Down Arrow
	
	areaData[cw][cd][offset+2] = v;
	
	[fieldObjectItem setStringValue:[self hexToString:v]];
	[fieldObjectItem selectText:nil];
	
	NSString *name = [self getObjectName:areaData[cw][cd][offset+1] position:areaData[cw][cd][offset] item:areaData[cw][cd][offset+2] world:cw];
	[fieldObjects setStringValue:[NSString stringWithFormat:@"%02X - %@",co,name]];
	
	[self redraw];
}

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

# pragma mark -

- (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]];
	}
	
	NSString *name = [self getEnemyName:t world:cw];
	[fieldEnemies setStringValue:[NSString stringWithFormat:@"%02X - %@",ce,name]];
}

- (IBAction)changeEnemyPosition:(id)sender		// Tag 00 + 01 02 03 04
{
	unsigned char t = [sender tag];
	unsigned char v = [self stringToHex:[fieldEnemyPosition stringValue]];
	unsigned char offset = 1 + (ce * 2);
	
	if (t == 0) {							// Direct Field Input
		enemyData[cw][ceg][offset] = v;
	}
	
	if (t == 1 && (v & 0x0F) > 0) {			// Left Arrow
		v -= 1;
		enemyData[cw][ceg][offset] = v;
	}
	
	if (t == 2 && (v & 0x0F) < 0x0F) {		// Right Arrow
		v += 1;
		enemyData[cw][ceg][offset] = v;
	}
	
	if (t == 3 && (v & 0xF0) > 0) {			// Up Arrow
		v -= 0x10;
		enemyData[cw][ceg][offset] = v;
	}
	
	if (t == 4 && (v & 0xF0) < 0xF0) {		// Down Arrow
		v += 0x10;
		enemyData[cw][ceg][offset] = v;
	}
	
	[fieldEnemyPosition setStringValue:[self hexToString:v]];
	[fieldEnemyPosition selectText:nil];
	
	[self redraw];
}

- (IBAction)changeEnemyType:(id)sender
{
	unsigned char t = [sender tag];
	unsigned char v = [self stringToHex:[fieldEnemyType stringValue]];
	unsigned char offset = 1 + (ce * 2);
	
	if (v > 0x3F) {
		NSBeep();
		v = 0x3F;
	}
	
	v &= 0x3F;
	
	if (t == 1 && v < 0x3F) v += 1;		// Up Arrow
	if (t == 2 && v > 0) v -= 1;		// Down Arrow
	
	enemyData[cw][ceg][offset+1] &= 0xC0;
	enemyData[cw][ceg][offset+1] |= v;
	
	[fieldEnemyType setStringValue:[self hexToString:v]];
	[fieldEnemyType selectText:nil];
	
	NSString *name = [self getEnemyName:v world:cw];
	[fieldEnemies setStringValue:[NSString stringWithFormat:@"%02X - %@",ce,name]];
	
	[self setNeedsDisplay:YES];
}

- (IBAction)changeEnemyScreen:(id)sender
{
	unsigned char t = [sender tag];
	unsigned char v = [self stringToHex:[fieldEnemyScreen stringValue]];
	unsigned char offset = 1 + (ce * 2);
	
	if (v > 3) {
		NSBeep();
		v = 3;
	}
	
	if (t == 1 && v < 3) v += 1;		// Up Arrow
	if (t == 2 && v > 0) v -= 1;		// Down Arrow
	
	enemyData[cw][ceg][offset+1] &= 0x3F;
	enemyData[cw][ceg][offset+1] |= v << 6;
	
	[fieldEnemyScreen setStringValue:[self hexToString:v]];
	[fieldEnemyScreen selectText:nil];
	
	[self setNeedsDisplay:YES];
}

# pragma mark -

- (IBAction)changeNoCeiling:(id)sender
{
	unsigned char v = [sender state];
	v <<= 7;
	
	areaData[cw][cd][2] &= 0x7F;
	areaData[cw][cd][2] |= v;
	
	[self redraw];
}

- (IBAction)changeExtraLayer:(id)sender
{
	unsigned char v = [self stringToHex:[fieldExtraLayer stringValue]];
	if (v > 7) v = 7;
	
	areaData[cw][cd][1] &= 0xE3;
	areaData[cw][cd][1] |= v << 2;
	
	[fieldExtraLayer setStringValue:[self hexToString:v]];
	[fieldExtraLayer selectText:nil];
	
	[self redraw];
}

- (IBAction)changeInitialCF:(id)sender
{
	unsigned char v = [self stringToHex:[fieldInitialCF stringValue]];
	if (v > 0x0F) v = 0x0F;
	
	[fieldInitialCF setStringValue:[self hexToString:v]];
	[fieldInitialCF selectText:nil];
	
	areaData[cw][cd][2] &= 0xF0;
	areaData[cw][cd][2] |= v;
	
	[self redraw];
}

- (IBAction)changeAreaWidth:(id)sender
{
	unsigned char v = [self stringToHex:[fieldAreaWidth stringValue]];
	if (v > 3) v = 3;
	areaWidth = v;
	
	areaData[cw][cd][1] &= 0x9F;
	areaData[cw][cd][1] |= v << 5;
	
	[fieldAreaWidth setStringValue:[self hexToString:v]];
	[fieldAreaWidth selectText:nil];
	
	[self setNeedsDisplay:YES];
}

- (IBAction)changeEnterCode:(id)sender
{
	unsigned char v = [self stringToHex:[fieldEnterCode stringValue]];
	if (v > 3) v = 3;
	enterCode = v;
	
	[fieldEnterCode setStringValue:[self hexToString:v]];
	[fieldEnterCode selectText:nil];
	
	[self setNeedsDisplay:YES];
}

- (IBAction)changeTileSet:(id)sender
{
	unsigned char v = [self stringToHex:[fieldTileSet stringValue]];
	if (v > 7) v = 7;
	
	areaData[cw][cd][2] &= 0x8F;
	areaData[cw][cd][2] |= v << 4;
	
	[fieldTileSet setStringValue:[self hexToString:v]];
	[fieldTileSet selectText:nil];
	
	[self redraw];
}

- (IBAction)changePaletteCode:(id)sender
{
	unsigned char v = [self stringToHex:[fieldPalette stringValue]];
	if (v > 7) v = 7;
	
	areaData[cw][cd][3] &= 0xC7;
	areaData[cw][cd][3] |= v << 3;
	
	[fieldPalette setStringValue:[self hexToString:v]];
	
	[self loadTiles];
	[self redraw];
}

- (IBAction)changeBGMap:(id)sender
{
	unsigned char v = [self stringToHex:[fieldBGMap stringValue]];
	if (v > 7) v = 7;
	
	areaData[cw][cd][3] &= 0xF8;
	areaData[cw][cd][3] |= v;
	
	[fieldBGMap setStringValue:[self hexToString:v]];
	
	[self loadTiles];
	[self redraw];
}

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

- (IBAction)move:(id)sender		// Left Down Up Right
{
	int t = [sender tag];
	unsigned int v = connectivity[cw][ca][t];
	
	if (v >= 0xFC) {
		// return to Overworld and check for 00-03
		NSRunAlertPanel([NSString stringWithFormat:@"Return to Overworld (%02X)",(v & 0x03)],@"",@"OK",@"",@"");
		return;
		// To do : Make a question, with choices "Return", "Cancel", and select the corresponding area in the OW Editor
	}
	
	v >>= 2;
	if (v == ca) return;
	
	[fieldArea setStringValue:[self hexToString:v]];
	[self changeArea:fieldArea];
}

- (IBAction)enterDoor:(id)sender
{
	int t = [sender selectedColumn];
	unsigned int v = doorDestinations[ca][t] >> 2;
	
	fromDoor = doorDestinations[ca][t] & 0x03;
	
	[fieldArea setStringValue:[self hexToString:v]];
	[self changeArea:fieldArea];
}

- (IBAction)changeGraphicsCode:(id)sender
{
	if ([sender tag] == 0) {
		regionCode = [self stringToHex:[fieldRegionCode stringValue]];
		if (regionCode > 2) regionCode = 2;
		[fieldRegionCode setStringValue:[self hexToString:regionCode]];
	} else {
		palaceCode = [self stringToHex:[fieldPalaceCode stringValue]];
		if (palaceCode > 7) palaceCode = 7;
		[fieldPalaceCode setStringValue:[self hexToString:palaceCode]];
	}
	
	[self loadTiles];
	[self loadTilesItems];
	[self loadTilesEnemies];
	[self loadTilesBosses];
	
	[self changeArea:fieldArea];
}

# pragma mark -

- (IBAction)selectEnemySet:(id)sender
{
	int i,j;
	
	int sc = [sender selectedColumn];
	int sr = [sender selectedRow];
	
	if (sc == 0) {
		[fieldEnemySet setStringValue:[self hexToString:sr]];
		[self changeEnemySet:fieldEnemySet];
	} else {
		for (i=0; i<0x7E; i++) {
			for (j=0; j<9; j++) {
				[[matrixEnemySets cellAtRow:i column:j] setBackgroundColor:[NSColor blackColor]];
			}
		}
		if (selectedEnemySet == sr && selectedEnemyInSet == sc) {
			selectedEnemySet = 0;
			selectedEnemyInSet = 0;
			[[matrixEnemySets selectedCell] setBackgroundColor:[NSColor blackColor]];
		} else {
			selectedEnemySet = sr;
			selectedEnemyInSet = sc;
			[[matrixEnemySets selectedCell] setBackgroundColor:[NSColor whiteColor]];
		}
	}
}

- (IBAction)changeEnemyInSet:(id)sender
{
	if (selectedEnemyInSet == 0) { NSBeep(); return; }
	
	unsigned char t = [[sender selectedCell] tag];
	unsigned char offset = 1 + ((selectedEnemyInSet - 1) * 2) + 1;
	
	enemyData[cw][selectedEnemySet][offset] &= 0xC0;
	enemyData[cw][selectedEnemySet][offset] |= t;
	[[matrixEnemySets cellAtRow:selectedEnemySet column:selectedEnemyInSet] setImage:[tilesEnemies objectAtIndex:t]];
}

# pragma mark -

/*- (void)displayObjectName:(unsigned char)objectCode position:(unsigned char)positionCode item:(unsigned char)itemCode world:(unsigned char)w
{
	NSString *str = [self getObjectName:objectCode position:positionCode item:itemCode world:w];
}*/

- (NSString *)getObjectName:(unsigned char)objectCode position:(unsigned char)positionCode item:(unsigned char)itemCode world:(unsigned char)w
{
	NSString *name = @"Undefined";
	
	// All 4 Overworld Regions
	
	if (w < 4) {
		if ((positionCode & 0xF0) == 0xF0) {	// Special Objects
			switch (objectCode & 0xF0) {
				case 0x20: name = @"Lava Pit, 3 high"; break;
				case 0x30: name = @"Cactus - Straight"; break;
				case 0x40: name = @"Cactus with Arm"; break;
				case 0x50: name = @"Elevator"; break;
				default: name = @"Undefined";
			}
		} 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 = @"Pit, extending to bottom"; 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 = @"Undefined";
			}
		} 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 = @"Undefined";
				}
			} 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 = @"Undefined";
				}
			}
		}
	}
	
	// Towns //
	
	if (w == 4) {
		if (objectCode < 0x10) {
			if ((positionCode & 0xF0) == 0xF0) {	// Special Small Objects
				switch (objectCode) {
					case 0x0: name = @"Portal to Left"; break;
					case 0x1: name = @"Portal to Right"; break;
					case 0x2: name = @"Wall to Left"; break;
					case 0x3: name = @"Wall to Right"; break;
					case 0x4: name = @"Fireplace"; break;
					case 0x5: name = @"Fountain"; break;
					case 0x6: name = @"Sign"; break;
					case 0x7: name = @"Gravestone"; break;
					case 0x8: name = @"Tomb Decoration - Left"; break;
					case 0x9: name = @"Tomb Decoration - Right"; break;
					default: name = @"Undefined";
				}
			} else {								// Small Objects
				switch (objectCode) {
					case 0x0: name = @"Closed Door with Grass"; break;
					case 0x1: name = @"Open Door with Grass"; break;
					case 0x2: name = @"Open Door with Lines"; break;
					case 0x3: name = @"Open Door with Bricks"; break;
					case 0x4: name = @"Undefined Object, 2 wide"; break;
					case 0x5: name = @"Chair"; break;
					case 0x6: name = @"Long Cloud"; break;
					case 0x7: name = @"Short Cloud"; break;
					case 0x8: name = @"Table"; break;
					case 0x9: name = @"Bench"; break;
					case 0xA: name = @"Cross"; break;
					case 0xB: name = @"Chimney - Can crouch in it"; break;
					case 0xC: name = @"Chimney - Can't go in"; break;
					case 0xD: name = @"Churchbell"; break;
					case 0xE: name = @"Locked Door"; break;
					case 0xF: name = @"Crash"; break;
					default: name = @"Undefined";
				}
			}
		} else {
			if ((positionCode & 0xF0) == 0xF0) {	// Special Variable Size Objects (Y Position = F)
				switch (objectCode & 0xF0) {
					case 0x10: name = @"Bushes, 1 high"; break;
					case 0x20: name = @"Background wall, 3 wide"; break;
					case 0x30: name = @"Indoor Ceiling, 3 high"; break;
					case 0x40: name = @"Indoor Ceiling, 3 high"; break;
					case 0x50: name = @"Confused Lumberjack"; break;
					case 0x60: name = @"Bridge with Water"; break;
					case 0x70: name = @"Water"; break;
					default: name = @"Undefined";
				}
			} else {								// Variable Size Objects
				switch (objectCode & 0xF0) {
					case 0x10: name = @"Cross Hatched Roof, 2 high"; break;
					case 0x20: name = @"Cross Hatched Roof, 2 high"; break;
					case 0x30: name = @"Shingle Roof, 2 high"; break;
					case 0x40: name = @"Shingle Roof, 2 high"; break;
					case 0x50: name = @"House Facade - Bricks"; break;
					case 0x60: name = @"House Facade - Small Bricks"; break;
					case 0x70: name = @"House Facade - Grass"; break;
					case 0x80: name = @"House Facade - Bricks - No Shadow"; break;
					case 0x90: name = @"House Facade - Logs"; break;
					case 0xA0: name = @"Square Window"; break;
					case 0xB0: name = @"Rounded Window"; break;
					case 0xC0: name = @"Hatched Window"; break;
					case 0xD0: name = @"Blocks Wall, 2 high"; break;
					case 0xE0: name = @"Column, 1 wide"; break;
					case 0xF0: name = @"Square Brick Wall, 1 high"; break;
					default: name = @"Undefined";
				}
			}
		}
	}
	
	// Palaces Type A and B //
	
	if (w == 5 || w == 6) {
		if ((positionCode & 0xF0) == 0xF0) {	// Special Objects
			switch (objectCode & 0xF0) {
				case 0x10: name = @"Lava Pit, 3 high"; break;
				case 0x20: name = @"Lava Pit, 3 high"; break;
				case 0x30: name = @"Lava Pit, 3 high"; break;
				case 0x40: name = @"Lava Pit, 3 high"; break;
				case 0x50: name = @"Elevator"; break;
				default: name = @"Undefined";
			}
		} else if (objectCode < 0x10) {			// Small Objects
			switch (objectCode) {
				case 0x00: name = @"Window"; break;
				case 0x01: name = @"Ra Statue"; break;
				case 0x02: name = @"Mau Statue"; break;
				case 0x03: name = @"Crystal Statue"; break;
				case 0x04: name = @"Crystal Statue"; break;
				case 0x05: name = @"Locked Door"; break;
				case 0x06: name = @"Locked Door"; break;
				case 0x07: name = @"Cloud - Long"; break;
				case 0x08: name = @"Cloud - Short"; break;
				case 0x09: name = @"Ironknuckle Statue"; break;
				case 0x0A: name = @"Cloud - Short"; break;
				case 0x0B: name = @"Cloud - Short"; break;
				case 0x0C: name = @"Cloud - Short"; break;
				case 0x0D: name = @"Cloud - Short"; break;
				case 0x0E: name = @"Cloud - Short"; break;
				case 0x0F: name = @"Cloud - Short"; break;
				default: name = @"Undefined";
			}
		} else {
			if (objSet == 0) {
				switch (objectCode & 0xF0) {		// Variable Size Objects - Set 0
					case 0x10: name = @"Horizontal Pit, 1 high"; break;
					case 0x20: name = @"Bricks, 1 high"; break;
					case 0x30: name = @"Breakable Blocks, 1 high"; break;
					case 0x40: name = @"Pedestal Blocks, 1 high"; break;
					case 0x50: name = @"Breaking Bridge, 1 high"; break;
					case 0x60: name = @"Bridge, 2 high"; break;
					case 0x70: name = @"Bricks, 2 high"; break;
					case 0x80: name = @"Curtains, 2 high"; break;
					case 0x90: name = @"Breakable Blocks, 2 high"; break;
					case 0xA0: name = @"False Bricks (pass through), 2 high"; break;
					case 0xB0: name = @"Breakable Blocks, 1 wide"; break;
					case 0xC0: name = @"Column, 1 wide"; break;
					case 0xD0: name = @"Vertical Pit, 1 wide"; break;
					case 0xE0: name = @"Vertical Pit, 1 wide"; break;
					case 0xF0: name = @"Pit, extending to bottom, 2 high"; break;
					default: name = @"Undefined";
				}
			} else {
				switch (objectCode & 0xF0) {		// Variable Size Objects - Set 1 (invalid)
					default: name = @"Undefined";
				}
			}
		}
	}
	
	// Great Palace //
	
	if (cw == 7) {
		if ((positionCode & 0xF0) == 0xF0) {	// Special Objects
			switch (objectCode & 0xF0) {
				case 0x10: name = @"Lava Pit, 2 high"; break;
				case 0x20: name = @"Lava Pit, 2 high"; break;
				case 0x30: name = @"Lava Pit, 2 high"; break;
				case 0x40: name = @"Lava Pit, 2 high"; break;
				case 0x50: name = @"Elevator"; break;
				default: name = @"Undefined";
			}
		} else if (objectCode < 0x10) {			// Small Objects
			switch (objectCode) {
				case 0x00: name = @"Window"; break;
				case 0x01: name = @"Ra Statue"; break;
				case 0x02: name = @"Mau Statue"; break;
				case 0x03: name = @"Mau Statue"; break;
				case 0x04: name = @"Mau Statue"; break;
				case 0x05: name = @"Locked Door"; break;
				case 0x06: name = @"Pit, extending to bottom, 1 wide"; break;
				case 0x07: name = @"Cloud - Long"; break;
				case 0x08: name = @"Cloud - Short"; break;
				case 0x09: name = @"Zelda"; break;
				case 0x0A: name = @"Fokka Statue"; break;
				case 0x0B: name = @"Cloud - Short"; break;
				case 0x0C: name = @"Cloud - Short"; break;
				case 0x0D: name = @"Cloud - Short"; break;
				case 0x0E: name = @"Cloud - Short"; break;
				case 0x0F: name = @"Cloud - Short"; break;
			}
		} else {
			if (objSet == 0) {
				switch (objectCode & 0xF0) {		// Variable Size Objects - Set 0
					case 0x10: name = @"Canopy, 1 high"; break;
					case 0x20: name = @"Bricks, 1 high"; break;
					case 0x30: name = @"Breakable Blocks, 1 high"; break;
					case 0x40: name = @"Pedestal Blocks, 1 high"; break;
					case 0x50: name = @"North Castle Bricks, 1 high"; break;
					case 0x60: name = @"North Castle Carpet, 1 high"; break;
					case 0x70: name = @"Breaking Bridge, 1 high"; break;
					case 0x80: name = @"Bridge, 2 high"; break;
					case 0x90: name = @"Bricks, 2 high"; break;
					case 0xA0: name = @"Curtains, 2 high"; break;
					case 0xB0: name = @"False Bricks (pass through), 2 high"; break;
					case 0xC0: name = @"Breakable Blocks, 2 high"; break;
					case 0xD0: name = @"Breakable Blocks, 1 wide"; break;
					case 0xE0: name = @"Electric Barrier Background, 1 wide"; break;
					case 0xF0: name = @"Column, 1 wide"; break;
					default: name = @"Undefined";
				}
			} else {
				switch (objectCode & 0xF0) {		// Variable Size Objects - Set 1 (invalid)
					default: name = @"Undefined";
				}
			}
		}
	}
	
	if (objectCode == 0xFF) [fieldObjects setStringValue:@"No Objects"];
	if ((positionCode & 0xF0) == 0xD0) {
		if (objectCode & 0x80) name = [NSString stringWithFormat:@"New C/F : %X + No Ceiling",(objectCode & 0x7F)];
		else name = [NSString stringWithFormat:@"New C/F : %X",objectCode];
	}
	if ((positionCode & 0xF0) == 0xE0) name = [NSString stringWithFormat:@"Skip to Screen %X",(positionCode & 0x03)];
	
	if ((positionCode & 0xF0) < 0xD0 && objectCode == 0x0F) {
		switch (itemCode) {
			case 0x00: name = @"Candle"; break;
			case 0x01: name = @"Glove"; break;
			case 0x02: name = @"Raft"; break;
			case 0x03: name = @"Boot"; break;
			case 0x04: name = @"Flute"; break;
			case 0x05: name = @"Cross"; break;
			case 0x06: name = @"Hammer"; break;
			case 0x07: name = @"Magical Key"; break;
			case 0x08: name = @"Key"; break;
			case 0x09: name = @"Doll (glitched)"; break;
			case 0x0A: name = @"Experience Bag - 50 Pts"; break;
			case 0x0B: name = @"Experience Bag - 100 Pts"; break;
			case 0x0C: name = @"Experience Bag - 200 Pts"; break;
			case 0x0D: name = @"Experience Bag - 500 Pts"; break;
			case 0x0E: name = @"Magic Container"; break;
			case 0x0F: name = @"Heart Container"; break;
			case 0x10: name = @"Blue Jar"; break;
			case 0x11: name = @"Red Jar"; break;
			case 0x12: name = @"Doll"; break;
			case 0x13: name = @"Kidnapped Child"; break;
			case 0x14: name = @"Trophy"; break;
			case 0x15: name = @"Medicine"; break;
			default: name = @"Undefined";
		}
	}
	
	return name;
}

- (NSString *)getEnemyName:(unsigned char)enemyCode world:(unsigned char)w
{
	NSString *name;
	
	if (w == 0 || w == 1) {		// West Hyrule / Death Mountain
		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 Generator"; break;
			case 0x0C: name = @"Rock Generator"; break;
			case 0x0D: name = @"Red Deeler"; break;
			case 0x0E: name = @"Blue Deeler"; break;
			case 0x0F: name = @"Bago Bago Generator"; 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 Generator"; break;
			case 0x22: name = @"Dumb Moblin"; break;
			default: name = [NSString stringWithFormat:@"Enemy ID %02X",enemyCode];
		}
	}
	
	if (w == 2 || w == 3) {		// East Hyrule / Maze Island
		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 Generator"; break;
			case 0x0C: name = @"Rock Generator"; break;
			case 0x0D: name = @"Red Deeler"; break;
			case 0x0E: name = @"Blue Deeler"; break;
			case 0x0F: name = @"Bago Bago Generator"; break;
			case 0x10: name = @"Bago Bago"; break;
			case 0x11: name = @"Blue Octorok - Jumping"; break;
			case 0x12: name = @"Blue Octorok - Moving"; break;
			case 0x13: name = @"Elevator"; break;
			case 0x14: name = @"Tektite"; break;
			case 0x15: name = @"Girobokku"; break;
			case 0x16: name = @"Leever"; break;
			case 0x17: name = @"Boon"; break;
			case 0x18: name = @"Zora"; break;
			case 0x19: name = @"Aru Lowder"; break;
			case 0x1A: name = @"Lizalfos - Red"; break;
			case 0x1B: name = @"Lizalfos - Orange"; break;
			case 0x1C: name = @"Lizalfos - Blue"; break;
			case 0x1D: name = @"Lizalfos - Rock Tossing"; break;
			default: name = [NSString stringWithFormat:@"Enemy ID %02X",enemyCode];
		}
	}
	
	if (w == 4) {		// Towns
		switch (enemyCode) {
			case 0x00: name = @"Fairy"; break;
			case 0x01: name = @"Red Jar"; break;
			case 0x02: name = @"Locked Door"; break;
			case 0x03: name = @"Ache"; break;
			case 0x04: name = @"Bot"; break;
			case 0x05: name = @"Bit"; break;
			case 0x06: name = @"Moa"; break;
			case 0x07: name = @"Acheman"; break;
			case 0x08: name = @"Orange Lady - Walking"; break;
			case 0x09: name = @"Red Lady - Walking"; break;
			case 0x0A: name = @"Ache (talker)"; break;
			case 0x0B: name = @"Bit (talker)"; break;
			case 0x0C: name = @"Dialog"; break;
			case 0x0D: name = @"Blue Old Woman - Immobile"; break;
			case 0x0E: name = @"Blue Lumberjack - Immobile"; break;
			case 0x0F: name = @"Wise Man"; break;
			case 0x10: name = @"Little Kid - Immobile"; break;
			case 0x11: name = @"Blue Lady with Bowl - Immobile"; break;
			case 0x12: name = @"Red Old Woman - Immobile"; break;
			case 0x13: name = @"Red Lumberjack - Idle"; break;
			case 0x14: name = @"Blue Lady with Bowl - Idle"; break;
			case 0x15: name = @"Blue Old Woman - Idle"; break;
			case 0x16: name = @"Red Lady - Idle"; break;
			case 0x17: name = @"Red Lady - Healing Lady"; break;
			case 0x18: name = @"Orange Old Woman - Magic Woman"; break;
			case 0x19: name = @"Blue Lumberjack - Walking"; break;
			case 0x1A: name = @"Red Lady with Bowl - Walking"; break;
			case 0x1B: name = @"Red Old Woman - Walking"; break;
			case 0x1C: name = @"Blue Lady - Walking"; break;
			case 0x1D: name = @"Blue Lumberjack - Walking"; break;
			case 0x1E: name = @"Red Young Lad - Walking"; break;
			case 0x1F: name = @"Red Young Lad - Walking"; break;
			case 0x20: name = @"Blue Lady - Walking"; break;
			case 0x21: name = @"Dialog"; break;
			case 0x22: name = @"Dialog"; break;
			case 0x23: name = @"Townfolks Generator"; break;
			default: name = [NSString stringWithFormat:@"Enemy ID %02X",enemyCode];
		}
	}
	
	if (w == 5 || w == 6) {		// Palaces Type A/B *** some enemies are different between A and B ***
		switch (enemyCode) {
			case 0x00: name = @"Fairy"; break;
			case 0x01: name = @"Palace Item"; break;
			case 0x02: name = @"Locked Door"; break;
			case 0x03: name = @"Myu"; break;
			case 0x04: name = @"Bot"; break;
			case 0x05: name = @"Hidden Red Jar"; break;
			case 0x06: name = @"Bubble - Slow"; break;
			case 0x07: name = @"Moa - Fire Dropping"; break;
			case 0x08: name = @"Falling Blocks Generator"; break;
			case 0x09: name = @"Falling Block"; break;
			case 0x0A: name = @"Ra"; break;
			case 0x0B: name = @"Tinsuit Generator"; break;
			case 0x0C: name = @"Tinsuit"; break;
			case 0x0D: name = @"Dripping Column"; break;
			case 0x0E: name = @"Bubble - Fast"; break;
			case 0x0F: if (w == 5) name = @"Bago Bago Generator"; else name = @"Ra"; break;
			case 0x10: if (w == 5) name = @"Bago Bago"; else name = @"Flame"; break;
			case 0x11: name = @"Rope - Jumping"; break;
			case 0x12: name = @"Rope - Moving"; break;
			case 0x13: name = @"Elevator"; break;
			case 0x14: name = @"Crystal Slot"; break;
			case 0x15: name = @"Crystal"; break;
			case 0x16: name = @"Energy Ball Shooter - Right"; break;
			case 0x17: name = @"Energy Ball Shooter - Left"; break;
			case 0x18: name = @"Iron Knuckle - Orange"; break;
			case 0x19: name = @"Iron Knuckle - Red"; break;
			case 0x1A: name = @"Iron Knuckle - Blue"; break;
			case 0x1B: name = @"Mau Generator"; break;
			case 0x1C: name = @"Mau"; break;
			case 0x1D: if (w == 5) name = @"Mago"; else name = @"Wizzrobe"; break;
			case 0x1E: if (w == 5) name = @"Guma"; else name = @"Doomknocker"; break;
			case 0x1F: name = @"Stalfos - Red"; break;
			case 0x20: if (w == 5) name = @"Horsehead"; else name = @"Rebonack"; break;
			case 0x21: if (w == 5) name = @"Helmethead / Gooma"; else name = @"Barba"; break;
			case 0x22: if (w == 5) name = @"Floating Helmet"; else name = @"Carock"; break;
			case 0x23: name = @"Stalfos - Blue"; break;
			default: name = [NSString stringWithFormat:@"Enemy ID %02X",enemyCode];
		}
	}
	
	if (w == 7) {				// Great Palace
		switch (enemyCode) {
			case 0x00: name = @"Fairy"; break;
			case 0x01: name = @"Palace Item"; break;
			case 0x02: name = @"Locked Door"; break;
			case 0x03: name = @"Myu"; break;
			case 0x04: name = @"Bot"; break;
			case 0x05: name = @"Hidden Red Jar"; break;
			case 0x06: name = @"Moa - Free Roaming"; break;
			case 0x07: name = @"Ache"; break;
			//case 0x08: name = @"?"; break;	// Mega Glitch
			//case 0x09: name = @"?"; break;	// Fast Glitch
			case 0x0A: name = @"Acheman"; break;
			case 0x0B: name = @"Bubbles Generator"; break;	// Uses Energy Ball sprite
			case 0x0C: name = @"Rocks Generator"; break;	// Uses Energy Ball sprite
			case 0x0D: name = @"Red Deeler"; break;			// Glitched behavior
			case 0x0E: name = @"Blue Deeler"; break;		// Correct behavior, visual glitch
			case 0x0F: name = @"Fire Bago Bago Generator"; break;
			case 0x10: name = @"Fire Bago Bago"; break;
			case 0x11: name = @"Fire Rope - Jumping"; break;
			case 0x12: name = @"Fire Rope - Moving"; break;
			case 0x13: name = @"Elevator"; break;
			case 0x14: name = @"Bubble - Slow"; break;
			case 0x15: name = @"Bubble - Fast"; break;
			case 0x16: name = @"Ra"; break;
			case 0x17: name = @"Big Bubble"; break;
			case 0x18: name = @"Fokka - Orange"; break;		// Glitchy
			case 0x19: name = @"Fokka - Red"; break;
			case 0x1A: name = @"Fokka - Blue"; break;
			//case 0x1B: name = @"?"; break;
			//case 0x1C: name = @"?"; break;				// Crash
			case 0x1D: name = @"Fokkeru"; break;
			case 0x1E: name = @"Giant Bot"; break;
			//case 0x1F: name = @"?"; break;				// Crash
			case 0x20: name = @"Electric Barrier"; break;
			//case 0x21: name = @"?"; break;				// Crash
			case 0x22: name = @"Thunderbird"; break;
			case 0x23: name = @"Dark Link Battle Trigger"; break;
			default: name = [NSString stringWithFormat:@"Enemy ID %02X",enemyCode];
		}
	}
	
	if (enemyCode == 0xFF) name = @"No Enemies";
	if (name == nil) name = @"";
	return name;
}

# pragma mark -

- (IBAction)changeTileObject:(id)sender
{
	if (sender == nil) teo = 1;
	else teo = [sender selectedRow] + 1;
	
	int i;
	unsigned char t,p;
	unsigned char tc0,tc1;
	unsigned char palCode0,palCode1;
	unsigned int tm0,tm1;
	
	if (sender == nil) {
		for (i=1; i<0x10; i++) {
			t = tileCodes[cw][objSet][(i * 2)];
			p = (t & 0xC0) >> 6;
			t &= 0x3F;
			[[matrixTileCodes cellAtRow:(i-1) column:0] setImage:[tilesEditor objectAtIndex:t+(p*0x40)]];
			[[matrixTileCodes cellAtRow:(i-1) column:0] setBordered:NO];
			t = tileCodes[cw][objSet][(i * 2) + 1];
			p = (t & 0xC0) >> 6;
			t &= 0x3F;
			[[matrixTileCodes cellAtRow:(i-1) column:1] setImage:[tilesEditor objectAtIndex:t+(p*0x40)]];
			[[matrixTileCodes cellAtRow:(i-1) column:1] setBordered:NO];
		}
	}
	
	// Tile 0 //
	
	tc0 = tileCodes[cw][objSet][(teo * 2)];
	palCode0 = (tc0 & 0xC0) >> 6;
	tc0 &= 0x3F;
	
	tm0 = tileMappings[cw][(palCode0 * 0x100) + (tc0 * 4) + 0] + (palCode0 * 0x100);
	[[matrixTiles01 cellAtRow:0 column:1] setImage:[tiles32x32 objectAtIndex:tm0]];
	tm0 = tileMappings[cw][(palCode0 * 0x100) + (tc0 * 4) + 1] + (palCode0 * 0x100);
	[[matrixTiles01 cellAtRow:1 column:1] setImage:[tiles32x32 objectAtIndex:tm0]];
	tm0 = tileMappings[cw][(palCode0 * 0x100) + (tc0 * 4) + 2] + (palCode0 * 0x100);
	[[matrixTiles01 cellAtRow:0 column:2] setImage:[tiles32x32 objectAtIndex:tm0]];
	tm0 = tileMappings[cw][(palCode0 * 0x100) + (tc0 * 4) + 3] + (palCode0 * 0x100);
	[[matrixTiles01 cellAtRow:1 column:2] setImage:[tiles32x32 objectAtIndex:tm0]];
	
	// Tile 1 //
	
	tc1 = tileCodes[cw][objSet][(teo * 2) + 1];
	palCode1 = (tc1 & 0xC0) >> 6;
	tc1 &= 0x3F;
	
	tm1 = tileMappings[cw][(palCode1 * 0x100) + (tc1 * 4) + 0] + (palCode1 * 0x100);
	[[matrixTiles01 cellAtRow:0 column:4] setImage:[tiles32x32 objectAtIndex:tm1]];
	tm1 = tileMappings[cw][(palCode1 * 0x100) + (tc1 * 4) + 1] + (palCode1 * 0x100);
	[[matrixTiles01 cellAtRow:1 column:4] setImage:[tiles32x32 objectAtIndex:tm1]];
	tm1 = tileMappings[cw][(palCode1 * 0x100) + (tc1 * 4) + 2] + (palCode1 * 0x100);
	[[matrixTiles01 cellAtRow:0 column:5] setImage:[tiles32x32 objectAtIndex:tm1]];
	tm1 = tileMappings[cw][(palCode1 * 0x100) + (tc1 * 4) + 3] + (palCode1 * 0x100);
	[[matrixTiles01 cellAtRow:1 column:5] setImage:[tiles32x32 objectAtIndex:tm1]];
	
	tm0 = tileCodes[cw][objSet][(teo * 2)];
	tm1 = tileCodes[cw][objSet][(teo * 2) + 1];
	
	// Vertical Single //
	
	for (i=0; i<4; i++) {
		[[matrixVerticalSingle cellAtRow:i column:0] setImage:[tilesEditor objectAtIndex:tm0]];
	}
	
	// Vertical Two //
	
	[[matrixVerticalTwo cellAtRow:0 column:0] setImage:[tilesEditor objectAtIndex:tm0]];
	for (i=1; i<4; i++) {
		[[matrixVerticalTwo cellAtRow:i column:0] setImage:[tilesEditor objectAtIndex:tm1]];
	}
	
	// Horizontal Single //
	
	for (i=0; i<4; i++) {
		[[matrixHorizontalSingle cellAtRow:0 column:i] setImage:[tilesEditor objectAtIndex:tm0]];
	}
	
	// Horizontal Double //
	
	for (i=0; i<4; i++) {
		[[matrixHorizontalDouble cellAtRow:0 column:i] setImage:[tilesEditor objectAtIndex:tm0]];
		[[matrixHorizontalDouble cellAtRow:1 column:i] setImage:[tilesEditor objectAtIndex:tm1]];
	}
	
	// Change Arrow Position //
	
	NSRect newPosition = tileCodesArrowBasePosition;
	newPosition.origin.y -= [sender selectedRow] * ([sender cellSize].height + [sender intercellSpacing].height);
	[buttonTileCodesArrow setFrame:newPosition];
}

- (IBAction)changeSelectionTSA:(id)sender
{
	teTSA = [sender selectedTag];
	
	[[matrixTiles01 cellAtRow:0 column:0] setImage:nil];
	[[matrixTiles01 cellAtRow:0 column:3] setImage:nil];
	[[matrixTiles01 cellAtRow:0 column:6] setImage:nil];
	
	[[matrixTiles01 cellAtRow:1 column:0] setImage:nil];
	[[matrixTiles01 cellAtRow:1 column:3] setImage:nil];
	[[matrixTiles01 cellAtRow:1 column:6] setImage:nil];
	
	unsigned char r = [sender selectedRow];
	
	if ([sender selectedTag] == 0 || [sender selectedTag] == 1) [[matrixTiles01 cellAtRow:r column:1] setImage:[NSImage imageNamed:@"right"]];
	if ([sender selectedTag] == 2 || [sender selectedTag] == 3) [[matrixTiles01 cellAtRow:r column:2] setImage:[NSImage imageNamed:@"left"]];
	if ([sender selectedTag] == 4 || [sender selectedTag] == 5) [[matrixTiles01 cellAtRow:r column:4] setImage:[NSImage imageNamed:@"right"]];
	if ([sender selectedTag] == 6 || [sender selectedTag] == 7) [[matrixTiles01 cellAtRow:r column:5] setImage:[NSImage imageNamed:@"left"]];
}

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

# pragma mark -

- (void)mouseDown:(NSEvent *)event
{
	NSInteger 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 bg;
	
	objx = 0;
	objy = 0;
	
	// Fill Screen with Background Tile //
	
	layerGroup = areaData[cw][cd][2];
	layerGroup &= 0x70;
	layerGroup >>= 4;
	
	if (layerGroup == 0) bg = 0x42;
	else bg = 0x40;
	
	for (i=0; i<[self numberOfRows]; i++) {
		for (j=0; j<[self numberOfColumns]; j++) {
			[[self cellAtRow:i column:j] setImage:[tiles objectAtIndex:bg]];
		}
	}
	
	// Background Area Data //
	
	bg = areaData[cw][cd][3] & 0x07;
	if (bg > 0) [self redrawAreaData:bg];
	
	// Area Data
	
	[self redrawAreaData:0];
}

- (void)redrawAreaData:(unsigned char)bgCode
{
	int i;
	unsigned char b1,b2,b3;
	unsigned char nb;
	unsigned char bank;
	//unsigned char c = 0;	// For Object Selection Box (TO DO)
	
	if (cw == 0 || cw == 1) bank = 0;
	if (cw == 2 || cw == 3) bank = 1;
	if (cw == 4) bank = 2;
	if (cw == 5 || cw == 6) bank = 3;
	if (cw == 7) bank = 4;
	
	for (i=0; i<4; i++) { skip[i] = 0; }
	for (i=0; i<4; i++) { item[i][0] = 0xF0; }
	
	if (bgCode == 0) {
		cfCode = areaData[cw][cd][2];
		nb = areaData[cw][cd][0];
	} else {
		cfCode = bgAreaData[bank][bgCode][2];
		nb = bgAreaData[bank][bgCode][0];
	}
	
	// Draw Background Ceiling/Floor //
	
	cfOffset = 0;
	cfLimit = 0;
	
	objx = 0;
	objy = 0;
	
	elevatorSlot = 0;
	lockedDoorSlot = 0;
	
	for (i=4; i<nb; i+=2) {
		
		if (bgCode == 0) {
			b1 = areaData[cw][cd][i];
			b2 = areaData[cw][cd][i+1];
			b3 = areaData[cw][cd][i+2];					//NSLog(@"b1b2 : %X %X",b1,b2);
		} else {
			b1 = bgAreaData[bank][bgCode][i];
			b2 = bgAreaData[bank][bgCode][i+1];
			b3 = bgAreaData[bank][bgCode][i+2];			//NSLog(@"b1b2 : %X %X",b1,b2);
		}
		
		objx += b1 & 0x0F;
		objy = b1 >> 4;
		
		if (objy < 0x0D && b2 == 0x0F) {	// Collectable Item
			//NSLog(@"Item");
			i++; continue;
		}
		
		if (objy == 0x0D) {					// New Default CF
			//NSLog(@"New CF %02X %02X",b1,b2);
			cfLimit = objx;
			[self drawCF];
			cfCode = b2;
			continue;
		}
		
		if (objy == 0x0E) {					// Skip Object
			//NSLog(@"Skip %02X",b1 & 3);
			objx = (b1 & 3) * 0x10;
			cfLimit = objx;
			[self drawCF];
			continue;
		}
	}
	
	if (cfOffset < 0x40) {
		cfLimit = 0x40; //NSLog(@"CF Remaining");
		[self drawCF];
	}
	
	// Draw Objects //
	
	objx = 0;
	objy = 0;
	
	for (i=4; i<nb; i+=2) {
		
		if (bgCode == 0) {
			b1 = areaData[cw][cd][i];
			b2 = areaData[cw][cd][i+1];
			b3 = areaData[cw][cd][i+2];					//NSLog(@"b1b2 : %X %X",b1,b2);
		} else {
			b1 = bgAreaData[bank][bgCode][i];
			b2 = bgAreaData[bank][bgCode][i+1];
			b3 = bgAreaData[bank][bgCode][i+2];			//NSLog(@"b1b2 : %X %X",b1,b2);
		}
		
		objy = b1 >> 4;
		objx += b1 & 0x0F;
		type = b2 >> 4;
		length = b2 & 0x0F;
		
		if (objy < 0x0D && b2 == 0x0F) {		// Collectable Item
			item[(objx / 0x10)][0] = objx;
			item[(objx / 0x10)][1] = objy;
			item[(objx / 0x10)][2] = b3;
			//NSLog(@"Item %02X %02X",b1,b3);
			i++;
			continue;
		}
		
		if (objy == 0xD) continue;				// New Default CF
		
		if (objy == 0x0E) {						// Skip Object
			skip[(b1 & 3)] += 1;
			objx = (b1 & 3) * 0x10;
			continue;
		}
		
		// ARE YOU
		// POWERFUL
		// ENOUGH TO
		// CROSS THE
		// PITS OF
		// DESPAIR?
		
		// All Overworld (WH DM EH MI) //
		
		if (cw < 4) {
			
			if (objy < 0xD) {
				
				if (type > 0) {				// Variable Size Objects
					switch (type) {
						case 0x1: break;	// Crash (could be Palisade Pass in East Hyrule)
						case 0x2: case 0x3: case 0x4:
							[self setHorizontalDouble:tileCodes[cw][objSet][(type*2)] secondCode:tileCodes[cw][objSet][(type*2)+1]]; break;
						case 0x5: case 0x6: case 0x7: case 0x8: case 0x9: case 0xA: case 0xB: case 0xC:
							[self setHorizontalSingle:tileCodes[cw][objSet][(type*2)]]; break;
						case 0xD: [self setVerticalSingle:tileCodes[cw][objSet][(type*2)]]; break;	// Single tile code
						case 0xE: case 0xF:
							[self setVerticalSingle:tileCodes[cw][objSet][(type*2)] secondCode:tileCodes[cw][objSet][(type*2)+1]]; break;
					}
				}
				
				if (type == 0) {			// Small Objects
					switch (length) {
						case 0x0: ptr = [rom bytes] + 0x42F8 + 0x10; [self setSingle:(*ptr)]; break;	// Gravestone
						case 0x1: ptr = [rom bytes] + 0x42F6 + 0x10; [self setSingle:(*ptr)]; break;	// Cross
						case 0x2: ptr = [rom bytes] + 0x42F7 + 0x10; [self setSingle:(*ptr)]; break;	// Slanted Cross
						case 0x3: ptr = [rom bytes] + 0x42CC + 0x10; [self setVerticalTwo:(*ptr) secondCode:(*(ptr+5))]; break;		// Tree Stump
						case 0x4: [self setOther:DOLMEN]; break;
						case 0x5: lockedDoorSlot = objx; break;	// Locked Door
						case 0x6: ptr = [rom bytes] + 0x4346 + 0x10; [self setHorizontalTwo:(*ptr) secondCode:(*(ptr+4))]; break;	// Zelda
						case 0x7: ptr = [rom bytes] + 0x4346 + 0x10; [self setHorizontalTwo:(*ptr) secondCode:(*(ptr+4))]; break;	// Zelda
						case 0x8: [self setOther:PIT]; break;
						case 0x9: ptr = [rom bytes] + 0x4356 + 0x10; [self setHorizontalTwo:(*(ptr+8)) secondCode:(*ptr)]; break;	// Long Cloud
						case 0xA: case 0xB: case 0xC: case 0xD: case 0xE: case 0xF:
							ptr = [rom bytes] + 0x4357 + 0x10; [self setHorizontalTwo:(*(ptr+7)) secondCode:(*ptr)]; break;			// Short Cloud
					}
				}
			}
			
			if (objy == 0xF) {				// Special Objects (Y Position = F)
				switch (type) {
					case 0x0: break;							// Crash
					case 0x1: break;							// Crash
					case 0x2: [self setOther:0xF2]; break;		// Lava Pit
					case 0x3: [self setOther:0xF3]; break;		// Cactus - Straight
					case 0x4: [self setOther:0xF4]; break;		// Cactus with Arm
					case 0x5: elevatorSlot = objx;				// Elevator
				}
			}
		}
		
		// Towns //
		
		if (cw == 4) {
			if (type == 0) {
				if (objy == 0xF) {			// Special Small Objects
					switch (length) {
						case 0x0: case 0x1: case 0x2: case 0x3: [self setOther:length]; break;	// Portals and Walls
						case 0x4: [self setOther:0x04]; break;	// Fireplace
						case 0x5: [self setOther:0x05]; break;	// Fountain
						case 0x6: objy = 9; ptr = [rom bytes] + 0xDC1F + 0x10; [self setVerticalTwo:(*ptr) secondCode:(*(ptr+5))]; break;	// Sign
						case 0x7: case 0x8: case 0x9:			// Gravestone and Decorations (Left/Right)
							objy = 9;
							ptr = [rom bytes] + 0xDCA9 + (length - 7) + 0x10;
							[self setSingle:(*ptr)];
							break;
					}
				} else {					// Small Objects
					switch (length) {
						case 0x0: case 0x1: case 0x2: case 0x3: [self setOther:(length + 0x10)]; break;		// Doors (Closed/Open)
						case 0x4: break;	// Undefined Object, 2 wide, extending to ground
						case 0x5: ptr = [rom bytes] + 0xC681 + 0x10; [self setSingle:(*ptr)]; break;		// Chair
						case 0x6: case 0x7:																	// Clouds (Long/Short)
							ptr = [rom bytes] + 0xC666 + 0x10;
							[self setHorizontalTwo:(*(ptr+8)) secondCode:(*(ptr+(length-6)))];
							break;
						case 0x8:																			// Table
							ptr = [rom bytes] + 0xC2AE + 0x10;
							[self setHorizontalTwo:(*(ptr+3)) secondCode:(*(ptr+2))]; objy += 1;
							[self setHorizontalTwo:(*(ptr+1)) secondCode:(*(ptr+0))];
							break;
						case 0x9: [self setOther:0x19]; break;												// Bench
						case 0xA: ptr = [rom bytes] + 0xC680 + 0x10; [self setSingle:(*ptr)]; break;		// Cross
						case 0xB: case 0xC:																	// Chimney (Enterable/Solid)
							ptr = [rom bytes] + 0xDC5A + (length - 0x0B) + 0x10;
							[self setHorizontalTwo:(*ptr) secondCode:(*ptr)];
							break;
						case 0xD: ptr = [rom bytes] + 0xC682 + 0x10; [self setSingle:(*ptr)]; break;		// Churchbell
						case 0xE: break;	// Locked door with glitched tiles
						case 0xF: break;	// Crash
					}
				}
			} else {
				if (objy == 0xF) {			// Special Variable Size Objects (Y Position = F)
					switch (type) {
						case 0x1: objy = 0x0A; ptr = [rom bytes] + 0xCBA9 + 0x10; [self setHorizontalSingle:(*ptr)]; break;
						case 0x2: [self setOther:0xF2]; break;
						case 0x3: [self setOther:0xF3]; break;
						case 0x4: [self setOther:0xF3]; break;
						case 0x5: break;	// Confused Lumberjack (Bagu?)
						case 0x6:
							objy = 0x0A;
							ptr = [rom bytes] + 0xC363 + 0x10;
							[self setHorizontalTriple:(*(ptr+2)) secondCode:(*(ptr+1)) thirdCode:(*(ptr+0))];
							break;
						case 0x7:
							objy = 0x0B;
							ptr = [rom bytes] + 0xC315 + 0x10;
							[self setHorizontalDouble:(*ptr) secondCode:(*(ptr+0x15))];
							break;
					}
				} else {
					switch (type) {			// Variable Size Objects
						case 0x1: case 0x2: ptr = [rom bytes] + 0xC0FA + 0x10; [self setHorizontalDouble:(*ptr) secondCode:(*(ptr+4))]; break;
						case 0x3: case 0x4: ptr = [rom bytes] + 0xC0FA + (type - 2) + 0x10; [self setHorizontalDouble:(*ptr) secondCode:(*(ptr+4))]; break;
						case 0x5: case 0x6: case 0x7: case 0x8: case 0x9:
							ptr = [rom bytes] + 0xC14B + (type - 5) + 0x10; [self setHouseFacade:(*ptr) secondCode:(*(ptr+5))]; break;
						case 0xA: case 0xB: case 0xC:
							ptr = [rom bytes] + 0xC186 + (type - 0x0A) + 0x10; [self setWindow:(*ptr) secondCode:(*(ptr+3))]; break;
						case 0xD: ptr = [rom bytes] + 0xC0FD + 0x10; [self setHorizontalDouble:(*ptr) secondCode:(*(ptr+4))]; break;
						case 0xE: ptr = [rom bytes] + 0xDC7A + 0x10; [self setVerticalSingle:(*ptr) secondCode:(*(ptr+6))]; break;
						case 0xF: ptr = [rom bytes] + 0xDC99 + 0x10; [self setHorizontalSingle:(*ptr)]; break;
					}
				}
			}
		}
		
		// Palaces Type A/B //
		
		if (cw == 5 || cw == 6) {
			if (objy < 0xD) {
				if (type > 0) {			// Variable Size Objects
					switch (type) {
						case 0x1: case 0x2: case 0x3: case 0x4: case 0x5:
							[self setHorizontalSingle:tileCodes[cw][objSet][(type*2)]]; break;
						case 0x6: case 0x7: case 0x8: case 0x9: case 0xA:
							[self setHorizontalDouble:tileCodes[cw][objSet][(type*2)] secondCode:tileCodes[cw][objSet][(type*2)+1]]; break;
						case 0xB: [self setVerticalSingle:tileCodes[cw][objSet][(type*2)]]; break;
						case 0xC: case 0xD: case 0xE:
							[self setVerticalSingle:tileCodes[cw][objSet][(type*2)] secondCode:tileCodes[cw][objSet][(type*2)+1]]; break;
						case 0xF:
							[self setOther:0]; break;
					}
				}
				
				if (type == 0) {		// Small Objects
					switch (length) {
						case 0x0: ptr = [rom bytes] + 0x10265 + 0x10; [self setVerticalTwo:(*(ptr+0)) secondCode:(*(ptr+4))]; break;	// Window
						case 0x1: ptr = [rom bytes] + 0x10265 + 0x10; [self setVerticalTwo:(*(ptr+2)) secondCode:(*(ptr+6))]; break;	// Ra Statue
						case 0x2: ptr = [rom bytes] + 0x10265 + 0x10; [self setVerticalTwo:(*(ptr+1)) secondCode:(*(ptr+5))]; break;	// Mau Statue
						case 0x3: case 0x4: [self setOther:length]; break;
						case 0x5: case 0x6: lockedDoorSlot = objx; break;	// Locked Door
						case 0x7: ptr = [rom bytes] + 0x102D3 + 0x10; [self setHorizontalTwo:(*(ptr+8)) secondCode:(*ptr)]; break;		// Long Cloud
						case 0x8: ptr = [rom bytes] + 0x102D4 + 0x10; [self setHorizontalTwo:(*(ptr+7)) secondCode:(*ptr)]; break;		// Short Cloud
						case 0x9: ptr = [rom bytes] + 0x10265 + 0x10; [self setVerticalTwo:(*(ptr+3)) secondCode:(*(ptr+7))]; break;	// Ironknuckle Statue
						case 0xA: case 0xB: case 0xC: case 0xD: case 0xE: case 0xF:
							ptr = [rom bytes] + 0x102D4 + 0x10; [self setHorizontalTwo:(*(ptr+7)) secondCode:(*ptr)]; break;			// Short Cloud
					}
				}
			}
			
			if (objy == 0xF) {			// Special Objects (Y Position = F)
				switch (type) {
					case 0x1: [self setOther:0xF1]; break;		// Lava Pit
					case 0x2: [self setOther:0xF1]; break;		// Lava Pit
					case 0x3: [self setOther:0xF1]; break;		// Lava Pit
					case 0x4: [self setOther:0xF1]; break;		// Lava Pit
					case 0x5: elevatorSlot = objx;				// Elevator
				}
			}
		}
		
		// Great Palace //
		
		if (cw == 7) {
			if (objy < 0xD) {
				if (type > 0) {			// Variable Size Objects
					switch (type) {
						case 0x1: case 0x2: case 0x3: case 0x4: case 0x5: case 0x6: case 0x7:
							[self setHorizontalSingle:tileCodes[cw][objSet][(type*2)]]; break;
						case 0x8: case 0x9: case 0xA: case 0xB: case 0xC:
							[self setHorizontalDouble:tileCodes[cw][objSet][(type*2)] secondCode:tileCodes[cw][objSet][(type*2)+1]]; break;
						case 0xD: case 0xE: [self setVerticalSingle:tileCodes[cw][objSet][(type*2)]]; break;
						case 0xF: [self setVerticalSingle:tileCodes[cw][objSet][(type*2)] secondCode:tileCodes[cw][objSet][(type*2)+1]]; break;
					}
				}
				
				if (type == 0) {		// Small Objects
					switch (length) {
						case 0x0: ptr = [rom bytes] + 0x14276 + 0x10; [self setVerticalTwo:(*ptr) secondCode:(*(ptr+4))]; break;	// Window
						case 0x1: ptr = [rom bytes] + 0x14277 + 0x10; [self setVerticalTwo:(*ptr) secondCode:(*(ptr+4))]; break;	// Ra Statue
						case 0x2: case 0x3: case 0x4:																				// Mau Statue
							ptr = [rom bytes] + 0x14278 + 0x10; [self setVerticalTwo:(*ptr) secondCode:(*(ptr+4))]; break;
						case 0x5: lockedDoorSlot = objx; break;		// Locked Door
						case 0x6: [self setOther:6]; break;			// Pit, extending to bottom, 1 wide
						case 0x7: ptr = [rom bytes] + 0x142B5 + 0x10; [self setHorizontalTwo:(*(ptr+8)) secondCode:(*ptr)]; break;	// Long Cloud
						case 0x8: ptr = [rom bytes] + 0x142B6 + 0x10; [self setHorizontalTwo:(*(ptr+7)) secondCode:(*ptr)]; break;	// Short Cloud
						case 0x9: ptr = [rom bytes] + 0x142A4 + 0x10; [self setHorizontalTwo:(*ptr) secondCode:(*(ptr+5))]; break;	// Zelda
						case 0xA: ptr = [rom bytes] + 0x14279 + 0x10; [self setVerticalTwo:(*ptr) secondCode:(*(ptr+4))]; break;	// Fokka Statue
						case 0xB: case 0xC: case 0xD: case 0xE: case 0xF:
							ptr = [rom bytes] + 0x142B6 + 0x10; [self setHorizontalTwo:(*(ptr+7)) secondCode:(*ptr)]; break;		// Short Cloud
					}
				}
			}
			
			if (objy == 0xF) {			// Special Objects (Y Position = F)
				switch (type) {
					case 0x1: [self setOther:0xF1]; break;		// Lava Pit
					case 0x2: [self setOther:0xF1]; break;		// Lava Pit
					case 0x3: [self setOther:0xF1]; break;		// Lava Pit
					case 0x4: [self setOther:0xF1]; break;		// Lava Pit
					case 0x5: elevatorSlot = objx;				// Elevator
				}
			}
		}
		
		//[self display];
		//NSLog(@"Draw Object %02X",(i-4)/2);
		//i = i;
	}
	
	/* All Tiles
	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)drawCF
{
	int i,j;
	
	unsigned char nc;		// No Ceiling Flag
	unsigned char mask;
	unsigned char b,v;
	unsigned char start;
	
	unsigned char bank;
	if (cw == 0 || cw == 1) bank = 0;
	if (cw == 2 || cw == 3) bank = 1;
	if (cw == 4) bank = 2;
	if (cw == 5 || cw == 6) bank = 3;
	if (cw == 7) bank = 4;
	
	if (cfLimit > 0x40) cfLimit = 0x40;
	
	nc = (cfCode >> 7); //NSLog(@"nc : %X",nc);
	
	ptr = [rom bytes] + 0x1C735 + ((cfCode & 0x0F) * 2) + 0x10; //NSLog(@"maskCode : %02X",*ptr);
	
	if (nc == 0) { mask = 0xE0; start = 0; }
	else { mask = 0x38; start = 2; }
	b = *ptr;
	for (i=start; i<6; i++) {
		v = (b & mask) >> (5 - i);
		mask >>= 1;
		//NSLog(@"Row %X : %02X (%02X)",i,v,tileLayers[cw][layerGroup][v]);
		for (j=cfOffset; j<cfLimit; j++) {
			if (tileLayers[bank][layerGroup][v] == 0x40) continue;
			if (tileLayers[bank][layerGroup][v] == 0x42) {
				if ([tiles indexOfObject:[[self cellAtRow:i column:j] image]] != 0x40) continue;
			}
			[[self cellAtRow:i column:j] setImage:[tiles objectAtIndex:tileLayers[bank][layerGroup][v]]]; //[self display];
		}
	}
	
	mask = 0xE0;
	b = *(ptr + 1);
	for (i=0; i<6; i++) {
		v = (b & mask) >> (5 - i);
		mask >>= 1;
		//NSLog(@"Row %X : %02X (%02X)",i+6,v,tileLayers[cw][layerGroup][v]);
		for (j=cfOffset; j<cfLimit; j++) {
			if (tileLayers[bank][layerGroup][v] == 0x40) continue;
			if (tileLayers[bank][layerGroup][v] == 0x42) {
				if ([tiles indexOfObject:[[self cellAtRow:i column:j] image]] != 0x40) continue;
			}
			[[self cellAtRow:i+6 column:j] setImage:[tiles objectAtIndex:tileLayers[bank][layerGroup][v]]]; //[self display];
		}
	}
	
	// Bottom Row //
	
	for (i=cfOffset; i<cfLimit; i++) {
		[[self cellAtRow:0xC column:i] setImage:[tiles objectAtIndex:tileLayers[bank][layerGroup][8]]]; //[self display];
	}
	
	// Extra Layer //
	
	unsigned char extraLayerHeight;
	unsigned char extraLayerTile;
	unsigned char extraLayerCode = (areaData[cw][cd][1] & 0x1C) >> 2;
	
	ptr = [rom bytes] + 0x1C4AF + extraLayerCode + 0x10;
	extraLayerHeight = *ptr;
	if (extraLayerHeight > 0) {
		extraLayerHeight >>= 4;
		ptr = [rom bytes] + 0x1C4A7 + extraLayerCode + 0x10;
		extraLayerTile = *ptr;
		for (i=cfOffset; i<cfLimit; i++) {
			[[self cellAtRow:extraLayerHeight column:i] setImage:[tiles objectAtIndex:extraLayerTile]]; //[self display];
		}
	}
	
	cfOffset = cfLimit;
	//[self display];
	
	//NSLog(@"----");
}

- (void)drawRect:(NSRect)theRect
{
	[super drawRect:[self frame]];
	
	if (!rom) return;
	
	int i;
	unsigned char b1,b2,b3;
	unsigned short x,y;
	unsigned short frameWidth,frameHeight;
	NSImage *enemyToDraw;
	NSPoint loc;
	
	NSGraphicsContext *gc = [NSGraphicsContext currentContext];
	
	// Draw Frame on Currently Selected Object //
	
	[gc setShouldAntialias:NO];
	
	x = 0;
	unsigned char offset = [self offsetForObject:co];
	BOOL isItem = NO;
	
	for (i=4; i<=offset; i+=2) {
		b1 = areaData[cw][cd][i];
		b2 = areaData[cw][cd][i+1];
		x += b1 & 0x0F;
		isItem = NO;
		if ((b1 & 0xF0) < 0xD0 && b2 == 0x0F) { i++; isItem = YES; continue; }	// Item
		if ((b1 & 0xF0) == 0xE0) x = (b1 & 0x0F) * 0x10;			// Skip
	}
	
	frameWidth = 1;
	frameHeight = 1;
	
	b1 = areaData[cw][cd][offset];
	b2 = areaData[cw][cd][offset+1];
	//NSLog(@"Index %02X : %02X %02X (%02X)",co,b1,b2,x);
	y = b1 & 0xF0;
	//if (y == 0xE0) { x = (b1 & 0x0F) * 0x10; y = 0; }
	
	objy = b1 >> 4;
	objx = b1 & 0x0F;
	type = b2 >> 4;
	length = b2 & 0x0F;
	
	if (type > 0) {				// Variable Size Objects
		switch (type) {
			case 0x1: break;	// Crash (could be Palisade Pass in East Hyrule)
			case 0x2: case 0x3: case 0x4:
				frameWidth = length + 1; frameHeight = 2; break;
			case 0x5: case 0x6: case 0x7: case 0x8: case 0x9: case 0xA: case 0xB: case 0xC:
				frameWidth = length + 1; frameHeight = 1; break;
			case 0xD: case 0xE: case 0xF:
				frameWidth = 1; frameHeight = length + 1; break;
		}
	}
	
	if (type == 0) {			// Small Objects
		switch (length) {
			case 0x0: case 0x1: case 0x2: frameWidth = 1; frameHeight = 1; break;	// Gravestone, Cross, Slanted Cross
			case 0x3: frameWidth = 1; frameHeight = 2; break;						// Tree Stump
			case 0x4: frameWidth = 4; frameHeight = 3; break;						// Dolmen
			case 0x5: frameWidth = 1; frameHeight = 3; break;						// Locked Door (*** TO DO xy exception ? ***)
			case 0x6: case 0x7: frameWidth = 2; frameHeight = 1; break;				// Zelda
			case 0x8: [self setOther:PIT]; break;	// TO DO extends to the ground
			case 0x9: frameWidth = 2; frameHeight = 1; break;														// Long Cloud
			case 0xA: case 0xB: case 0xC: case 0xD: case 0xE: case 0xF: frameWidth = 2; frameHeight = 1; break;		// Short Cloud
		}
	}
	
	if (objy == 0xD) {				// New Ceiling/Floor
		y = 0;
		frameWidth = 0.5;
		frameHeight = 0x0D;
	}
	
	if (objy == 0xF) {				// Special Objects (Y Position = F)
		switch (type) {
			//case 0x0: case 0x1: break;		// Crash
			case 0x2: y = 0xA0; frameWidth = length + 1; frameHeight = 3; break;	// Lava Pit
			case 0x3: y = 0x70; frameWidth = 1; frameHeight = 3; break;				// Cactus - Straight
			case 0x4: y = 0x80; frameWidth = 1; frameHeight = 2; break;				// Cactus with Arm
			case 0x5: y = 0x70; frameWidth = 2; frameHeight = 4; break;	// Elevator (*** TO DO xy exception ? ***)
		}
	}
	
	if (isItem) {		// Items
		frameWidth = 1;
	}

	frameWidth *= 16;
	frameHeight *= 16;

	[[NSColor colorWithDeviceWhite:1.0 alpha:1.0] set];
	//[[NSColor cyanColor] set];
	[NSBezierPath strokeRect:NSMakeRect(x * 16 , y , frameWidth , frameHeight )];
	
	if (objy == 0xD) {
		
		[gc setShouldAntialias:YES];
		
		NSBezierPath *triangle = [NSBezierPath bezierPath];
		[triangle moveToPoint:NSMakePoint(x * 16 , 112)];
		[triangle lineToPoint:NSMakePoint(x * 16 + 8 , 104)];
		[triangle lineToPoint:NSMakePoint(x * 16 , 96)];
		[triangle closePath];
		[triangle fill];
		
		[gc setShouldAntialias:NO];
	}
	
	/* Draw Link (not perfect, height needs to be adjusted according to ground level)
	
	NSImage *link = [tilesLink objectAtIndex:!(enterCode & 1)];
	
	if (enterCode == 0) { loc.x = 0x1F; loc.y = 0xB1; }
	if (enterCode == 1) { loc.x = 0x1F; loc.y = 0xB1; }
	if (enterCode == 2) { loc.x = 0x1F; loc.y = 0xB1; }
	if (enterCode == 3) { loc.x = 0x1F; loc.y = 0xB1; }
	
	[link compositeToPoint:loc operation:NSCompositeSourceOver];
	*/
	
	// Draw Skip Objects //
	
	for (i=0; i<4; i++) {
		if (skip[i] == 0) continue;
		loc.x = i * 0x100;
		loc.y = 0x10;
		[[NSImage imageNamed:@"Skip.png"] compositeToPoint:loc operation:NSCompositeSourceOver fraction:0.7];
	}
	
	// Draw Enemies //
	
	unsigned char w = (regionCode * 4) + palaceCode;
	unsigned char n;
	
	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;
		if (n >= [tilesEnemies count]) enemyToDraw = [NSImage imageNamed:@"Undefined.png"];
		else enemyToDraw = [tilesEnemies objectAtIndex:n];
		
		// Check if Enemy is a Boss
		
		if (cw > 4 && n == 0x20) {
			if (cw == 5) enemyToDraw = [tilesBosses objectAtIndex:0];		// Horsehead
			else enemyToDraw = [tilesBosses objectAtIndex:2];				// Rebonack
		}
		if (cw > 4 && n == 0x21) {
			if (w == 1) enemyToDraw = [tilesBosses objectAtIndex:1];		// Helmethead
			if (w == 8) enemyToDraw = [tilesBosses objectAtIndex:4];		// Gooma
			if (w == 9) enemyToDraw = [tilesBosses objectAtIndex:5];		// Barba
		}
		if (cw > 4 && n == 0x22) {
			if (w == 1) enemyToDraw = [tilesEnemies objectAtIndex:0x22];	// Floating Helmet
			if (w == 4) enemyToDraw = [tilesBosses objectAtIndex:3];		// Carock
			if (w == 0x0A) enemyToDraw = [tilesBosses objectAtIndex:6];		// Thunderbird
		}
		if (n == 0x23 && w == 0x0A) enemyToDraw = [tilesBosses objectAtIndex:7];	// Dark Link Battle Trigger
		
		loc.y += [enemyToDraw size].height;
		
		if (cw == 6 && n == 0x10) { loc.x += 4; loc.y += 1; }	// Adjustment for Flame (x+4 / y+1)
		if (n == 0x14) loc.x += 0x18;							// Arbitrary adjustment for Crystal Slot
		
		if (cw == 5 && n == 0x20) loc.y += 1;					// Adjustment for Horsehead (y+1)
		if (cw >= 5 && n == 0x21 && w == 1) loc.y -= 0x0F;		// Adjustment for Helmethead (y-0x0F)
		if (cw == 6 && n == 0x20) loc.y -= 4;					// Adjustment for Rebonack (y-4)
		if (cw >= 5 && n == 0x22 && w == 4) loc.y += 1;			// Adjustment for Carock (y+1)
		if (cw >= 5 && n == 0x21 && w == 8) loc.y += 1;			// Adjustment for Gooma (y+1)
		if (cw == 6 && n == 0x21) loc.y -= 56;					// Adjustment for Barba (y-56)
		
		// Make Dialog objects semi-transparent //
		
		if (cw == 4 && (n == 0x0C || n == 0x21 || n == 0x22)) [enemyToDraw compositeToPoint:loc operation:NSCompositeSourceOver fraction:0.6];
		else if (enemyToDraw == [NSImage imageNamed:@"Undefined.png"]) [enemyToDraw compositeToPoint:loc operation:NSCompositeSourceOver fraction:0.5];
		else [enemyToDraw compositeToPoint:loc operation:NSCompositeSourceOver];
	}
	
	// TO DO : False Wall Overlay
	
	// Draw Items //
	
	unsigned char s = (regionCode * 5) + worldCode; //NSLog(@"Section : %02X",s);	// Needs work, worldCode must be from Overworld Bytes
	//s = 4;	// arbitrary value for now, Palaces Type A
	
	unsigned char itemPresence;
	unsigned char mask;
	
	for (i=0; i<4; i++) {
		
		if (item[i][0] == 0xF0) continue;
		
		b1 = item[i][0];
		b2 = item[i][1];
		b3 = item[i][2];
		
		itemPresence = itemBits[s][(ca >> 1)]; //NSLog(@"itemPresence : %02X",itemPresence);
		mask = 0x08;
		if (!(ca & 1)) mask <<= 4;
		mask >>= i; //NSLog(@"mask : %02X",mask);
		itemPresence &= mask;
		if (itemPresence == 0) continue;
		
		loc.x = b1 * 0x10;
		loc.y = (b2 + 1) * 0x10;
		if (b3 >= [tilesItems count]) [[NSImage imageNamed:@"Undefined.png"] compositeToPoint:loc operation:NSCompositeSourceOver];
		else [[tilesItems objectAtIndex:b3] compositeToPoint:loc operation:NSCompositeSourceOver];
	}
	
	// Draw Locked Door (if any and presence bit is 1)
	
	if (lockedDoorSlot > 0) {
		
		itemPresence = itemBits[s][(ca >> 1)]; //NSLog(@"itemPresence : %02X",itemPresence);
		mask = 0x08;
		if (!(ca & 1)) mask <<= 4;
		mask >>= (lockedDoorSlot >> 4); //NSLog(@"mask : %02X",mask);
		itemPresence &= mask;
		
		if (itemPresence != 0) {
			loc.x = lockedDoorSlot * 0x10;
			ptr = [rom bytes] + 0x1119 + 0x10;	// Locked Door set to Y Position 0xA0
			loc.y = *ptr;
			loc.y += 0x10;	// Arbitrary adjustment
			[[tilesEnemies objectAtIndex:0x02] compositeToPoint:loc operation:NSCompositeSourceOver];
		}
	}
	
	// Draw Elevator (if any) //
	
	if (elevatorSlot > 0) {
		loc.x = elevatorSlot * 0x10;
		ptr = [rom bytes] + 0x115D + 0x10;	// X Position Adjust
		loc.x += *ptr;
		ptr = [rom bytes] + 0x116A + 0x10;	// Elevator set to Y Position 0x98 by default (different if Link rides it)
		loc.y = *ptr;
		loc.y += 0x11;	// Arbitrary adjustment
		[[tilesEnemies objectAtIndex:0x13] compositeToPoint:loc operation:NSCompositeSourceOver];
	}
	
	// Draw Transparent Sheet Overlay //
	
	NSRect sheetRect = [self frame];
	[[NSColor colorWithDeviceWhite:0 alpha:0.7] set];	//[[NSColor colorWithDeviceRed:1.0 green:0.2 blue:0.2 alpha:0.2] set];
	
	if (areaWidth < 3) {
		if (areaWidth == 0) {
			if (enterCode == 0) {
				sheetRect.origin.x = 0x100;
				sheetRect.size.width = 0x300;
				[NSBezierPath fillRect:sheetRect];
			}
			else if (enterCode == 3) {
				sheetRect.origin.x = 0;
				sheetRect.size.width = 0x300;
				[NSBezierPath fillRect:sheetRect];
			} else if (enterCode == 1) {
				sheetRect.origin.x = 0;
				sheetRect.size.width = 0x100;
				[NSBezierPath fillRect:sheetRect];
				sheetRect.origin.x = 0x200;
				sheetRect.size.width = 0x200;
				[NSBezierPath fillRect:sheetRect];
			} else {
				sheetRect.origin.x = 0;
				sheetRect.size.width = 0x200;
				[NSBezierPath fillRect:sheetRect];
				sheetRect.origin.x = 0x300;
				sheetRect.size.width = 0x100;
				[NSBezierPath fillRect:sheetRect];
			}
		} else {
			sheetRect.origin.x = (areaWidth + 1) * 0x100;
			sheetRect.size.width = (3 - areaWidth) * 0x100;
			[NSBezierPath fillRect:sheetRect];
		}
	}
	
	// Draw Screen Separator Lines //
	
	[[NSColor colorWithDeviceWhite:1.0 alpha:0.8] set];
	for (i=1; i<4; i++) {
		[NSBezierPath strokeLineFromPoint:NSMakePoint( i * 0x100 , 0 ) toPoint:NSMakePoint( i * 0x100 , [self frame].size.height )];
	}
	
	// Test
	
	//for (i=0; i<[tilesItems count]; i++) {
		//[[tilesItems objectAtIndex:i]  compositeToPoint:NSMakePoint(16 + i * 32, 96) operation:NSCompositeSourceOver];
	//}
}

# pragma mark -

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

- (void)setHorizontalTwo:(unsigned char)c1 secondCode:(unsigned char)c2
{
	[[self cellAtRow:objy column:objx] setImage:[tiles objectAtIndex:c1]];
	[[self cellAtRow:objy column:objx+1] setImage:[tiles objectAtIndex:c2]];
}

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

- (void)setHorizontalDouble:(unsigned char)c1 secondCode:(unsigned char)c2
{
	int i;
	for (i=0; i<=length; i++) {
		[[self cellAtRow:objy+0 column:objx+i] setImage:[tiles objectAtIndex:c1]];
		[[self cellAtRow:objy+1 column:objx+i] setImage:[tiles objectAtIndex:c2]];
	}
}

- (void)setHorizontalTriple:(unsigned char)c1 secondCode:(unsigned char)c2 thirdCode:(unsigned char)c3
{
	int i;
	for (i=0; i<=length; i++) {
		[[self cellAtRow:objy+0 column:objx+i] setImage:[tiles objectAtIndex:c1]];
		[[self cellAtRow:objy+1 column:objx+i] setImage:[tiles objectAtIndex:c2]];
		[[self cellAtRow:objy+2 column:objx+i] setImage:[tiles objectAtIndex:c3]];
	}
}

/*
- (void)setVerticalUp:(unsigned char)c1 secondCode:(unsigned char)c2
{
	int i;
	for (i=0; i<length; i++) {
		[[self cellAtRow:objy-i column:objx] setImage:[tiles objectAtIndex:c1]];
	}
	[[self cellAtRow:objy-i column:objx] setImage:[tiles objectAtIndex:c2]];
}
*/

- (void)setVerticalTwo:(unsigned char)c1 secondCode:(unsigned char)c2
{
	[[self cellAtRow:objy column:objx] setImage:[tiles objectAtIndex:c1]];
	[[self cellAtRow:objy+1 column:objx] setImage:[tiles objectAtIndex:c2]];
}

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

- (void)setVerticalSingle:(unsigned char)c1 secondCode:(unsigned char)c2
{
	int i;
	[[self cellAtRow:objy column:objx] setImage:[tiles objectAtIndex:c1]];
	for (i=1; i<=length; i++) {
		[[self cellAtRow:objy+i column:objx] setImage:[tiles objectAtIndex:c2]];
	}
}

- (void)setVerticalDouble:(unsigned char)code
{
	while (objy <= 0x0C) {	// Extend to bottom of screen
		[[self cellAtRow:objy column:objx] setImage:[tiles objectAtIndex:code]];
		[[self cellAtRow:objy column:objx+1] setImage:[tiles objectAtIndex:code]];
		objy++;
	}
}

- (void)setHouseFacade:(unsigned char)c1 secondCode:(unsigned char)c2
{
	int i;
	while (objy < 0xB) {		// Height of floor hard-coded to B0
		for (i=0; i <= length; i++) {
			if (i == length) [[self cellAtRow:objy column:objx+i] setImage:[tiles objectAtIndex:c2]];
			else [[self cellAtRow:objy column:objx+i] setImage:[tiles objectAtIndex:c1]];
		}
		objy++;
	}
}

- (void)setWindow:(unsigned char)c1 secondCode:(unsigned char)c2
{
	while (objy < 10) {
		[[self cellAtRow:objy column:objx] setImage:[tiles objectAtIndex:c1]];
		[[self cellAtRow:objy+1 column:objx] setImage:[tiles objectAtIndex:c2]];
		objy += 3;
	}
}

- (void)setOther:(unsigned char)code
{
	int i,j;
	
	// Overworld (West Hyrule, Death Mountain, East Hyrule, Maze Island)
	
	if (cw < 4) {
		switch (code) {
			case 0x04:		// Dolmen
				ptr = [rom bytes] + 0x4304 + 0x10;
				for (i=0; i<3; i++) {
					for (j=0; j<0x10; j++) {
						if ((*ptr) == 0xFF) { ptr++; break; }
						[[self cellAtRow:objy+i column:objx+j] setImage:[tiles objectAtIndex:(*ptr++)]];
					}
				}
				break;
			case 0x08:		// Pit
				while (objy < 0xD) {
					ptr = [rom bytes] + 0x4335 + 0x10;
					[[self cellAtRow:objy column:objx] setImage:[tiles objectAtIndex:(*ptr)]];	// Background Tile
					objy++;
				}
				break;
			case 0xF2:		// Lava Pit
				objy = 9;
				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 0xF3:		// Straight Cactus
				objy = 9;
				[[self cellAtRow:objy-length column:objx] setImage:[tiles objectAtIndex:0x46]];
				while (objy < 0xB) {
					[[self cellAtRow:objy-length+1 column:objx] setImage:[tiles objectAtIndex:0x47]];
					objy++;
				}
				break;
			case 0xF4:		// Cactus with Arm
				objy = 9;
				[[self cellAtRow:objy-1 column:objx] setImage:[tiles objectAtIndex:0x48]];
				[[self cellAtRow:objy-0 column:objx] setImage:[tiles objectAtIndex:0x49]];
				break;
			//
		}
	}
	
	// Towns
	
	if (cw == 4) {
		switch (code) {
			case 0x00: case 0x01: case 0x02: case 0x03:		// Portals and Walls (Left/Right) (Y Position = 2)
				ptr = [rom bytes] + 0xC1FC + (code * 0x1B) + 0x1A + 0x10;
				for (i=0; i<3; i++) {
					objy = 2;
					for (j=0; j<9; j++) {
						if ((*ptr) != 0x44) [[self cellAtRow:objy column:objx+i] setImage:[tiles objectAtIndex:(*ptr)]];
						objy++; ptr--;
						//[self display];
					}
				}
				break;
			case 0x04:				// Fireplace
				objy = 9;
				ptr = [rom bytes] + 0xC2C9 + 5 + 0x10;
				for (i=0; i<2; i++) {
					[[self cellAtRow:objy+0 column:objx+i] setImage:[tiles objectAtIndex:(*ptr--)]];
					[[self cellAtRow:objy+1 column:objx+i] setImage:[tiles objectAtIndex:(*ptr--)]];
					[[self cellAtRow:objy+2 column:objx+i] setImage:[tiles objectAtIndex:(*ptr--)]];
				}
				break;
			case 0x05:				// Fountain
				objy = 9;
				ptr = [rom bytes] + 0xDC28 + 9 + 0x10;
				for (i=0; i<2; i++) {
					for (j=0; j<5; j++) {
						[[self cellAtRow:objy+i column:objx+j] setImage:[tiles objectAtIndex:(*ptr--)]];
					}
				}
				break;
			//
			case 0x10: case 0x11: case 0x12: case 0x13:		// Doors (00 01 02 03)
				ptr = [rom bytes] + 0xC394 + ((code - 0x10) * 4) + 3 + 0x10;
				for (i=0; i<4; i++) {
					[[self cellAtRow:objy+i column:objx] setImage:[tiles objectAtIndex:(*ptr--)]];
				}
				break;
			case 0x19:
				ptr = [rom bytes] + 0xC68E + 0x10;
				for (i=0; i<5; i++) {
					[[self cellAtRow:objy column:objx+i] setImage:[tiles objectAtIndex:(*ptr++)]];
				}
				break;
			//
			case 0xF2:					// Brick Wall, 3 wide, X high (Y Position = 4)
				objy = 4;
				ptr = [rom bytes] + 0xC337 + 0x10;
				for (i=0; i<=length; i++) {
					[[self cellAtRow:objy+i column:objx+0] setImage:[tiles objectAtIndex:(*(ptr+2))]];
					[[self cellAtRow:objy+i column:objx+1] setImage:[tiles objectAtIndex:(*(ptr+1))]];
					[[self cellAtRow:objy+i column:objx+2] setImage:[tiles objectAtIndex:(*(ptr+0))]];
				}
				break;
			case 0xF3: case 0xF4:		// Indoor Ceiling, 3-Steps stairs with Tile Code 44 (C1C0/C1EA) (Y Position = 2)
				objy = 2;
				ptr = [rom bytes] + (code == 0xF3 ? 0xC1C0 : 0xC1EA) + 0x10;
				for (i=0; i<3; i++) {
					for (j=0; j<=length; j++) {
						if ((objx+i+j) > 0x3F) continue;
						[[self cellAtRow:objy+i column:objx+i+j] setImage:[tiles objectAtIndex:(*ptr)]];
					}
				}
				break;
			//
		}
	}
	
	// Palaces Type A/B
	
	if (cw == 5 || cw == 6) {
		switch (code) {
			case 0x0:										// Pit, extending to bottom, X wide
				ptr = [rom bytes] + 0x102C0 + 0x10;
				while (objy < 0xD) {
					for (i=0; i <= length; i++) {
						[[self cellAtRow:objy column:objx+i] setImage:[tiles objectAtIndex:(*ptr)]];
					}
					objy++;
				}
				break;
			case 0x3: case 0x4:								// Crystal Statue
				ptr = [rom bytes] + 0x1027E + 0x10;
				i = -1;
				while (i++ < 0x18) {
					if ((*ptr) == 0xFF) {
						objy++; ptr++; continue;
					}
					[[self cellAtRow:objy column:objx+(i % 5)] setImage:[tiles objectAtIndex:(*ptr++)]];
				}
				break;
			//
			case 0xF1:				// Lava Pit
				objy = 0x0B;
				ptr = [rom bytes] + 0x10219 + 0x10;
				for (i=0; i <= length; i++) {
					[[self cellAtRow:objy+0 column:objx+i] setImage:[tiles objectAtIndex:(*ptr)]];
					[[self cellAtRow:objy+1 column:objx+i] setImage:[tiles objectAtIndex:(*(ptr+0x17))]];
				}
				break;
			//
		}
	}
	
	// Great Palace
	
	if (cw == 7) {
		switch (code) {
			case 0x6:		// Pit, extending to bottom, 1 wide
				ptr = [rom bytes] + 0x14293 + 0x10;
				while (objy < 0xD) {
					[[self cellAtRow:objy column:objx] setImage:[tiles objectAtIndex:(*ptr)]];
					objy++;
				}
				break;
			case 0xF1:		// Lava Pit
				objy = 0x0B;
				ptr = [rom bytes] + 0x1422A + 0x10;
				for (i=0; i <= length; i++) {
					[[self cellAtRow:objy+0 column:objx+i] setImage:[tiles objectAtIndex:(*ptr)]];
					[[self cellAtRow:objy+1 column:objx+i] setImage:[tiles objectAtIndex:(*(ptr+0x17))]];
				}
				break;
			//
		}
	}
}

- (void)setItem:(unsigned char)code
{
	if (code >= [tilesItems count]) [[self cellAtRow:objy column:objx] setImage:[NSImage imageNamed:@"Undefined.png"]];
	else [[self cellAtRow:objy column:objx] setImage:[tilesItems objectAtIndex:code]];
}

# pragma mark -

- (unsigned char)currentWorld { return cw; }
- (NSArray *)tilesEnemies { return tilesEnemies; }

# 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

