+  RHDN Forum Archive
|-+  Romhacking
| |-+  ROM Hacking Discussion
| | |-+  Any docs on Link's Awakening?
Pages: [1]
Author Topic: Any docs on Link's Awakening?  (Read 1 times)
CyanPrime
Guest
« on: October 25, 2010, 06:20:13 pm »

I'm thinking about creating a LA moding program, but I am still sub-novice with hacking (yet good at programming) , so I'm hoping there are some docs for Link's Awakening. Looking on here I only see ALTTP.

Edit: just found the DX docs at the end. I'd delete this, but I don't see a way how.  Embarrassed
tcaudilllg
Guest
« Reply #1 on: October 25, 2010, 09:29:51 pm »

Just leave it. You're not the only one who would ask such a question, and here we have the answer.
Jandazekon
Guest
« Reply #2 on: October 26, 2010, 08:53:30 am »

CyanPrime.

have you seen this blog before???
http://zeldahacks.blogspot.com/
CyanPrime
Guest
« Reply #3 on: October 26, 2010, 02:08:50 pm »

Quote from: Jandazekon on October 26, 2010, 08:53:30 am
CyanPrime.

have you seen this blog before???
http://zeldahacks.blogspot.com/
Nope. never seen this before. Thank you. ^_^
snarfblam
Guest
« Reply #4 on: October 26, 2010, 05:28:10 pm »

I started writing a LADX editor once. I didn't get too far, but I can give the code I have if you would like. It's written in C#, so you should be able to read it pretty easily. I have no idea whether there's anything in there you haven't covered yet.
CyanPrime
Guest
« Reply #5 on: October 26, 2010, 06:00:05 pm »

Quote from: snarfblam on October 26, 2010, 05:28:10 pm
I started writing a LADX editor once. I didn't get too far, but I can give the code I have if you would like. It's written in C#, so you should be able to read it pretty easily. I have no idea whether there's anything in there you haven't covered yet.

Oh, I'm still doing research. I haven't even coded a window yet, so that level editor would really help. I found the source for another C# DX level editor, but it's a bit over my head, and there are no comments to say what the offsets in it do. x_x
snarfblam
Guest
« Reply #6 on: October 26, 2010, 07:40:29 pm »

Did you get that thing I sent you? (I.e. I PMed it to you)
« Last Edit: October 27, 2010, 05:48:59 pm by snarfblam »
CyanPrime
Guest
« Reply #7 on: October 26, 2010, 09:28:33 pm »

Quote from: snarfblam on October 26, 2010, 07:40:29 pm
Did you get that thing I sent you? (I.e. I'll PMed it to you)
Yeah, I got it. Thank you very much ^_^
Kagemusha
Guest
« Reply #8 on: October 26, 2010, 09:32:07 pm »

I'm not sure if this is helpful to you, but there's already an editor for LA called ZLADE. But I guess it's not perfect by any means.
CyanPrime
Guest
« Reply #9 on: October 26, 2010, 10:03:43 pm »

Quote from: Pennywise on October 26, 2010, 09:32:07 pm
I'm not sure if this is helpful to you, but there's already an editor for LA called ZLADE. But I guess it's not perfect by any means.
Yeah, I seen that one, but it's not open source, so it's not too much help. Thank you for trying though ^_^
Lin
Guest
« Reply #10 on: October 26, 2010, 10:20:16 pm »

There aren't really any docs, but here's some notes I took when Jigglysaint gave me stuff.

Code:
=Level editing (Dungeons)=
0x28000 - Pointers to rooms (Dungeons 1-6, and some caves). 2 bytes each
0x2C000 - Pointers to rooms (Dungeons 7-9, and rest caves). 2 bytes each
Example: 03 42 = 0x28203

Header = 2 bytes:
1st = Animation index: "set it to different values to control things like water animation and torch \t\tand lamp animations." This would make certain tiles animate.
2nd = (Nybble encoded) First 4 bits (0 in 0D) = Wall template (Defining floor and walls). For example, \t$93 = 9 = walls, 3 = floor type
Regular tiles = $00 - $EB
Door tiles = $EC - $FD
\t$F0 - $F3 = Closed door
\t$F4 - $F7 = Open door
2 Byte Objects
First byte = Location (YX) (3A = [Y=3][X=A])
Second byte = Tile ID

3 Byte Objects
Always start with 8x or Cx. X = How long it is
8 = Horizontal
C = Vertical
(Follow 2 byte format)

Warps
Each screen can have one warp

5-Byte Warps
First byte = Area Type (E tells it to be a warp, 2 = area type)
\t00 = overworld
\t01 = dungeon
\t02 = side view area
Second byte = Dungeon map (00 = Level 1, 01 = Level 2, etc)
Third byte = Dest Room (Must be in map of the dest dungeon)
Fourth byte = X (Tile x, extra X)
Fifth byte = Y (Add 10, starts at header)

Connections
Each dungeon has 64 (8x8) bytes for the connections. Starts at top left, and goes right. For example:
00 00 00 00 00 00 00 00 00
00 01 00 00 02 03 04 00 00

Starts at 0x50220
00 = No map (null map, enclosed room)
Anything other than 00 is the map ID.

Minimap
The minimap works the same as the connections (Almost)
Side-view maps do not show.
Starts at 0xA49A (ONLY WITH 1.1 ROM)
7D = Empty
EF = Normal Room
EE = Boss
ED = Chest

Sprites
Starts at 0x58000. All sprites are placed here.
First data is a list of pointers.
After finding sprite data, it works just like two byte objects (Refer to top)

Tilesets
0x805aa - Wall tile set
0x80589 - Floor tile set

Graphics
Contains no index. 4 bytes per tile, each indicating the 8x8 tiles it's composed of
0x0-0xF - Special Object Graphics
0x10-0x1F - Floor Tiles
0x20-0x3F - Wall Tiles
0x40-7F - Remainder (Chests, stairs, etc...)
0x6C-0x6F - Animated tile graphics, change depending on animation index
0xF0-0xFF - Pots, Blocks, and Statues
Combinations start at: 203b0
Floor graphics start at: 0xB4C00 (4 per tile)

Overworld Editing
Rooms 0-7F - 0x24000
Rooms 80-FF - 0x68000
Overlay data start: 98000 (At 0xCC, data starts at 0x9C000 instead of continuing from room 0xCB)
All overworld pointers are at 0x24000

Overworld Minimap Editing
Graphics 0x0 - 0x6F: 0xB3900
Graphics 0x70 - 0x7F: 0xB3800
Data starts at: 0x81697
Refers to minimap graphic index
Palette starts at: 0x81797

Overworld Palette Pointers: 0x6a476
Overworld sprite sets: 0x830D3 One byte per room

When I was going to create another editor for these games, I wrote a class to load the dungeon tilesets from the ROM. Perhaps you can make use of it.

Code:
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using GBHL;

namespace ZLA_Dungeon_Tileset_Viewer
{
\tpublic class TilesetLoader
\t{
\t\tGBFile gb;
\t\tprivate byte[] finalData = new byte[0x900];
\t\tColor[] bwPalette = new Color[] { Color.White, Color.LightGray, Color.DarkGray, Color.Black };
\t\tColor[,] palette = new Color[8, 4];

\t\tpublic struct Tile
\t\t{
\t\t\tpublic byte[] palette;
\t\t\tpublic bool[] hFlip;
\t\t\tpublic bool[] vFlip;
\t\t}

\t\tpublic TilesetLoader(GBFile g)
\t\t{
\t\t\tgb = g;
\t\t}

\t\tpublic byte[] loadFirstRow(int dungeon)
\t\t{
\t\t\t//0x8F00
\t\t\tif (dungeon == 0xFF) //0xFF = Color dungeon
\t\t\t{
\t\t\t\treturn gb.ReadBytes(0xC6100, 0x100);
\t\t\t}
\t\t\tgb.BufferLocation = 0x805CA + dungeon;
\t\t\tbyte b = gb.ReadByte();
\t\t\tgb.BufferLocation = 0xC8000 + ((b - 0x40) * 0x100);
\t\t\treturn gb.ReadBytes(0x100);
\t\t}

\t\tpublic byte[] loadSpecialGraphics(int dungeon, byte sog)
\t\t{
\t\t\t//0x9000
\t\t\tif (dungeon == 0xFF) //0xFF = Color dungeon
\t\t\t{
\t\t\t\treturn gb.ReadBytes(0xD6200, 0x100);
\t\t\t}
\t\t\tif (sog == 0xFF) //0xFF means don't load any others, just keep the existing data
\t\t\t\treturn new byte[0x100];
\t\t\tgb.BufferLocation = 0xB4000 + (sog + 0x10) * 0x100;
\t\t\treturn gb.ReadBytes(0x100);
\t\t}

\t\tpublic byte[] loadFloor(int dungeon)
\t\t{
\t\t\t//0x9100
\t\t\tif (dungeon == 0xFF) //0xFF = Color dungeon
\t\t\t{
\t\t\t\treturn gb.ReadBytes(0xD6000, 0x100);
\t\t\t}
\t\t\tgb.BufferLocation = 0x80589 + dungeon;
\t\t\tbyte b = gb.ReadByte();
\t\t\tgb.BufferLocation = 0xB4000 + ((b - 0x40) * 0x100);
\t\t\treturn gb.ReadBytes(0x100);
\t\t}

\t\tpublic byte[] loadRegular()
\t\t{
\t\t\t//0x9200-97FF
\t\t\t//This seems to be loaded even with the color dungeon, so we'll skip the dungeon check
\t\t\treturn gb.ReadBytes(0xB4000, 0x600);
\t\t}

\t\tpublic byte[] loadWalls(int dungeon)
\t\t{
\t\t\t//0x9200-93FF
\t\t\tif (dungeon == 0xFF) //0xFF = Color dungeon
\t\t\t{
\t\t\t\tgb.BufferLocation = 0x805C9;
\t\t\t}
\t\t\telse
\t\t\t\tgb.BufferLocation = 0x805A9 + dungeon;
\t\t\tbyte b = gb.ReadByte();
\t\t\tgb.BufferLocation = 0xB4000 + ((b - 0x40) * 0x100);
\t\t\treturn gb.ReadBytes(0x200);
\t\t}

\t\tpublic byte[] loadAnimation(byte animation)
\t\t{
\t\t\tgb.BufferLocation = 0x1BD5 + animation * 2;
\t\t\tgb.BufferLocation = gb.ReadByte() + (gb.ReadByte() * 0x100);
\t\t\twhile (gb.ReadByte() != 0x26) ;
\t\t\tbyte b = gb.ReadByte();
\t\t\tgb.BufferLocation = 0xB0000 + b * 0x100 - 0x4000;
\t\t\treturn gb.ReadBytes(0x40);
\t\t}

\t\tpublic byte[,] loadFormation()
\t\t{
\t\t\tgb.BufferLocation = 0x203B0;
\t\t\tbyte[,] data = new byte[0x100, 4];
\t\t\tfor (int i = 0; i < 0x100; i++)
\t\t\t{
\t\t\t\tfor (int k = 0; k < 4; k++)
\t\t\t\t{
\t\t\t\t\tdata[i, k] = gb.ReadByte();
\t\t\t\t}
\t\t\t}
\t\t\treturn data;
\t\t}

\t\tpublic byte[, ,] loadTileset(int dungeon, byte sog, byte animation, bool sideview, int map)
\t\t{
\t\t\tList<byte> final = new List<byte>();
\t\t\tbyte[] walls = new byte[0x200];
\t\t\tbyte[] animations = new byte[0x40];

\t\t\tforeach (byte b in loadFirstRow(dungeon))
\t\t\t\tfinal.Add(b);

\t\t\tif (!sideview)
\t\t\t{
\t\t\t\tforeach (byte b in loadSpecialGraphics(dungeon, sog))
\t\t\t\t\tfinal.Add(b);
\t\t\t\tforeach (byte b in loadFloor(dungeon))
\t\t\t\t\tfinal.Add(b);
\t\t\t\tforeach (byte b in loadRegular())
\t\t\t\t\tfinal.Add(b);
\t\t\t\twalls = loadWalls(dungeon);
\t\t\t\tanimations = loadAnimation(animation);

\t\t\t\tfor (int i = 0; i < 0x200; i++)
\t\t\t\t\tfinal[i + 0x300] = walls[i];
\t\t\t}
\t\t\telse
\t\t\t{
\t\t\t\tif ((dungeon < 10 || map == 0xE9) && dungeon != 6)
\t\t\t\t\tgb.BufferLocation = 0xB7800;
\t\t\t\telse
\t\t\t\t\tgb.BufferLocation = 0xB7000;
\t\t\t\tforeach (byte b in gb.ReadBytes(0x800))
\t\t\t\t\tfinal.Add(b);
\t\t\t}

\t\t\tfor (int i = 0; i < 0x40; i++)
\t\t\t{
\t\t\t\tfinal[(0x7C * 16) + i] = animations[i];
\t\t\t}

\t\t\tbyte[, ,] data = new byte[144, 8, 8];
\t\t\tgb.ReadTiles(16, 9, final.ToArray(), ref data);
\t\t\treturn data;
\t\t}

\t\tpublic Bitmap drawRawTiles(byte[, ,] graphicsData)
\t\t{
\t\t\tBitmap bmp = new Bitmap(128, 72);
\t\t\tFastPixel fp = new FastPixel(bmp);
\t\t\tfp.rgbValues = new byte[128 * 72 * 4];
\t\t\tfp.Lock();
\t\t\tfor (int i = 0; i < graphicsData.GetLength(0); i++)
\t\t\t{
\t\t\t\tfor (int y = 0; y < 8; y++)
\t\t\t\t{
\t\t\t\t\tfor (int x = 0; x < 8; x++)
\t\t\t\t\t{
\t\t\t\t\t\tfp.SetPixel(x + ((i % 16) * 8), y + ((i / 16) * 8), bwPalette[graphicsData[i, x, y]]);
\t\t\t\t\t}
\t\t\t\t}
\t\t\t}
\t\t\tfp.Unlock(true);
\t\t\treturn bmp;
\t\t}

\t\tpublic Bitmap drawPalette()
\t\t{
\t\t\tBitmap b = new Bitmap(64, 64);
\t\t\tGraphics g = Graphics.FromImage(b);
\t\t\tfor (int y = 0; y < 8; y++)
\t\t\t{
\t\t\t\tfor (int x = 0; x < 4; x++)
\t\t\t\t{
\t\t\t\t\tg.FillRectangle(new SolidBrush(palette[y, x]), x * 16, y * 8, 16, 8);
\t\t\t\t}
\t\t\t}
\t\t\treturn b;
\t\t}

\t\tpublic Tile[] loadPaletteFlipIndexes(int map, byte dungeon)
\t\t{
\t\t\tif (dungeon == 0xFF) //0xFF = Color dungeon
\t\t\t{
\t\t\t\tgb.BufferLocation = 0x8A000;
\t\t\t}
\t\t\telse if (dungeon < 9)
\t\t\t{
\t\t\t\tgb.BufferLocation = 0x6A076 + dungeon * 2;
\t\t\t\tgb.BufferLocation = 0x8C000 + gb.ReadByte() + ((gb.ReadByte() - 0x40) * 0x100);
\t\t\t}
\t\t\telse
\t\t\t{
\t\t\t\tgb.BufferLocation = 0x6A276 + dungeon * 2;
\t\t\t\tgb.BufferLocation = 0x90000 + gb.ReadByte() + ((gb.ReadByte() - 0x40) * 0x100);
\t\t\t}
\t\t\tTile[] tiles = new Tile[0x100];
\t\t\tfor (int i = 0; i < 0x100; i++)
\t\t\t{
\t\t\t\ttiles[i] = new Tile();
\t\t\t\ttiles[i].palette = new byte[4];
\t\t\t\ttiles[i].hFlip = new bool[4];
\t\t\t\ttiles[i].vFlip = new bool[4];
\t\t\t\tfor (int k = 0; k < 4; k++)
\t\t\t\t{
\t\t\t\t\tbyte b = gb.ReadByte();
\t\t\t\t\ttiles[i].palette[k] = (byte)(b & 0xF);
\t\t\t\t\tb = (byte)(b >> 4);
\t\t\t\t\tif ((b & 1) != 0)
\t\t\t\t\t\ttiles[i].vFlip[k] = true;
\t\t\t\t\tif ((b & 2) != 0)
\t\t\t\t\t\ttiles[i].hFlip[k] = true;
\t\t\t\t}
\t\t\t}
\t\t\treturn tiles;
\t\t}

\t\tpublic Bitmap drawTileset(byte[, ,] graphicsData, Tile[] tiles)
\t\t{
\t\t\tBitmap bmp = new Bitmap(256, 256);
\t\t\tFastPixel fp = new FastPixel(bmp);
\t\t\tfp.rgbValues = new byte[256 * 256 * 4];
\t\t\tfp.Lock();
\t\t\tbyte[,] formationData = loadFormation();
\t\t\tfor (int tile = 0; tile < 256; tile++)
\t\t\t{
\t\t\t\tfor (int corner = 0; corner < 4; corner++)
\t\t\t\t{
\t\t\t\t\tbyte i = formationData[tile, corner];
\t\t\t\t\tTile t = tiles[tile];
\t\t\t\t\tfor (int y = 0; y < 8; y++)
\t\t\t\t\t{
\t\t\t\t\t\tfor (int x = 0; x < 8; x++)
\t\t\t\t\t\t{
\t\t\t\t\t\t\tColor pal = palette[t.palette[corner] % 8, graphicsData[(byte)(i + 0x10), x, y]];//bwPalette[graphicsData[(byte)(i + 0x10), x, y]];
\t\t\t\t\t\t\tint mainX = (tile % 16) * 16;
\t\t\t\t\t\t\tint mainY = (tile / 16) * 16;
\t\t\t\t\t\t\tint xx = mainX + ((corner % 2) * 8) + x;
\t\t\t\t\t\t\tint yy = mainY + ((corner / 2) * 8) + y;
\t\t\t\t\t\t\tif (t.hFlip[corner])
\t\t\t\t\t\t\t\txx = mainX + ((corner % 2) * 8) + (7 - x);
\t\t\t\t\t\t\tif (t.vFlip[corner])
\t\t\t\t\t\t\t\tyy = mainY + ((corner / 2) * 8) + (y - y);
\t\t\t\t\t\t\tfp.SetPixel(xx, yy, pal);
\t\t\t\t\t\t}
\t\t\t\t\t}
\t\t\t\t}
\t\t\t}
\t\t\tfp.Unlock(true);
\t\t\treturn bmp;
\t\t}

\t\tpublic void loadPalette(int dungeon, int room, bool sideroom)
\t\t{
\t\t\t//Format - 4 bytes per set... dungeon, map, unknown, paletteindex
\t\t\t//The game loops through each of these (2D max), and if it comes
\t\t\t//across one with matching values as the current map, it loads the
\t\t\t//palette. They are common for dungeon entrances, from side-room to
\t\t\t//normal room, etc...
\t\t\t//We can load it the way like this, but for displaying purposes,
\t\t\t//we're going to go ahead and make it dungeon-specific, rather than
\t\t\t//room and treat C125 (how screens were switched, 4 being a warp) as 4.
\t\t\t//1 byte - dungeon
\t\t\t//1 byte - room
\t\t\t//1 byte - room switch type
\t\t\t//1 byte - encrypted palette index
\t\t\tif (!sideroom)
\t\t\t{
\t\t\t\tgb.BufferLocation = 0x8523A;
\t\t\t\tfor (int i = 0; i < 0x2D; i++)
\t\t\t\t{
\t\t\t\t\tif (gb.ReadByte() != dungeon)
\t\t\t\t\t{
\t\t\t\t\t\tgb.BufferLocation += 3;
\t\t\t\t\t\tcontinue;
\t\t\t\t\t}
\t\t\t\t\tgb.ReadByte(); //Map
\t\t\t\t\tbyte off = gb.ReadByte();
\t\t\t\t\tif (off != 4)
\t\t\t\t\t{
\t\t\t\t\t\tgb.BufferLocation++;
\t\t\t\t\t\tcontinue;
\t\t\t\t\t}
\t\t\t\t\tbyte b = gb.ReadByte();
\t\t\t\t\tgb.BufferLocation = 0x851F6;
\t\t\t\t\tb &= 0x3F;
\t\t\t\t\tb <<= 1;
\t\t\t\t\tgb.BufferLocation += b;
\t\t\t\t\tgb.BufferLocation = 0x84000 + gb.ReadByte() + ((gb.ReadByte() - 0x40) * 0x100);
\t\t\t\t\tpalette = gb.GetPalette(gb.BufferLocation);
\t\t\t\t\treturn;
\t\t\t\t}
\t\t\t}

\t\t\t//Basically that's the table/loading code for the room-specific loading
\t\t\t//If the palettes there don't contain a type 4, or entrance palette,
\t\t\t//then we do some other pointer stuff to get the right one.
\t\t\tif (dungeon == 0xFF) //0xFF = Color dungeon
\t\t\t{
\t\t\t\tgb.BufferLocation = 0x867D0;
\t\t\t\tpalette = gb.GetPalette(gb.BufferLocation);
\t\t\t\treturn;
\t\t\t}
\t\t\tif (dungeon > 9) //Indoor
\t\t\t{
\t\t\t\tgb.BufferLocation = 0x84413 + (dungeon - 10) * 2;
\t\t\t\tgb.BufferLocation = 0x84000 + gb.ReadByte() + ((gb.ReadByte() - 0x40) * 0x100) + room;
\t\t\t\tbyte b = (byte)(gb.ReadByte() * 2);
\t\t\t\tgb.BufferLocation = 0x8443F + b;
\t\t\t\tgb.BufferLocation = 0x84000 + gb.ReadByte() + ((gb.ReadByte() - 0x40) * 0x100);
\t\t\t\tpalette = gb.GetPalette(gb.BufferLocation);
\t\t\t\treturn;
\t\t\t}
\t\t\tint d = dungeon << 1;
\t\t\tif (sideroom)
\t\t\t{
\t\t\t\tif (dungeon != 7)
\t\t\t\t{
\t\t\t\t\tgb.BufferLocation = 0x84401 + d;
\t\t\t\t\tgb.BufferLocation = 0x84000 + gb.ReadByte() + ((gb.ReadByte() - 0x40) * 0x100);
\t\t\t\t\tpalette = gb.GetPalette(gb.BufferLocation);
\t\t\t\t}
\t\t\t\telse if ((room >= 0x64 && room <= 0x67) || room == 0x6A || room == 0x6B)
\t\t\t\t{
\t\t\t\t\tgb.BufferLocation = 0x86450;
\t\t\t\t\tpalette = gb.GetPalette(gb.BufferLocation);
\t\t\t\t}
\t\t\t\telse
\t\t\t\t{
\t\t\t\t\tgb.BufferLocation = 0x84401 + d;
\t\t\t\t\tgb.BufferLocation = 0x84000 + gb.ReadByte() + ((gb.ReadByte() - 0x40) * 0x100);
\t\t\t\t\tpalette = gb.GetPalette(gb.BufferLocation);
\t\t\t\t}
\t\t\t}
\t\t\telse
\t\t\t{
\t\t\t\tgb.BufferLocation = 0x843EF + d;
\t\t\t\tgb.BufferLocation = 0x84000 + gb.ReadByte() + ((gb.ReadByte() - 0x40) * 0x100);
\t\t\t\tpalette = gb.GetPalette(gb.BufferLocation);
\t\t\t}
\t\t}
\t}
}

Oh, and even if ZLADE was open-source, it'd be a nightmare to look at. I was a terrible programmer back then, so pretend it doesn't exist Smiley
Pages: [1]  


Powered by SMF 1.1.4 | SMF © 2006-2007, Simple Machines LLC