Primary Zelda Hacking
April 21, 2014, 09:26:36 PM *
Welcome, Guest. Please login or register.
Did you miss your activation email?

Login with username, password and session length
News: There is now an official Primary Zelda Hacking Press!
 
   Home   Help Search Login Register  
Pages: [1] 2
  Print  
Author Topic: ZOSE - Zelda Oracles Script Editor  (Read 2048 times)
Lin
ZOLE Creator
Administrator
Hero Member
*****
Online Online

Posts: 580



View Profile
« on: October 18, 2010, 09:06:02 AM »


~ZOSE is finished. Go to the ZOSE board to ask questions and discuss.~

http://zeldahacking.ulmb.com/index.php?board=25.0

Welcome to the topic for the 3rd planned tool for Zelda Oracles hacking. This tool will forever change hacking these games as much as ZOLE itself did, and make hacking super easy.

So what is ZOSE? ZOSE is an editor for a scripting language I'm developing that compiles to assembly code. Here will be an example script.
Code:
start 4B 02 ;Start at the default ASM address for the type 2 interaction 4B 02
setwrite 14C000 ;Set it to write out assembly code at a blank location
checktile 46 12 ;Check tile 46 to see if it's 12
retn ;Return if it's not 12
settile 46 13 ;Set tile 46 to 13 if it is
ret ;Return

Simple enough, right? Hopefully it all follows through. Everything has been falling into place, so let's hope it continues that way. I'll have screenshots as soon as possible.

~Lin
« Last Edit: February 20, 2011, 02:04:59 AM by Fatories » Logged
Fatories
LALE Creator
Administrator
Full Member
*****
Offline Offline

Posts: 223



View Profile
« Reply #1 on: October 18, 2010, 06:44:42 PM »

 :thumbup:

That is all.
Logged

The force of a true man can penetrate all. Nothing is safe.
Lin
ZOLE Creator
Administrator
Hero Member
*****
Online Online

Posts: 580



View Profile
« Reply #2 on: October 21, 2010, 11:06:31 PM »

Alright, well this is going to be re-worked a bit. Instead of inserting assembly, it will generate a script using the game's already existing scripting engine. It is currently only dungeon-only, but when have limits actually limited us? It will insert a custom assembly script to make them work outside of dungeons, which is a very simple hack. So keep your hopes up!
Logged
Lin
ZOLE Creator
Administrator
Hero Member
*****
Online Online

Posts: 580



View Profile
« Reply #3 on: October 22, 2010, 02:49:51 AM »

Big update: Decompiling (and also compiling), by interaction ID or address:

Logged
Lin
ZOLE Creator
Administrator
Hero Member
*****
Online Online

Posts: 580



View Profile
« Reply #4 on: October 23, 2010, 12:01:56 AM »

GREAT news! This may lead to being able to edit almost every interaction if my theories and tests are correct! No more being stuck with existing events! It seems that every type 2 (and other) interaction uses the scripting engine, so with a little emulation, ZOSE might be able to decompile/edit the scripts of regular interactions like people and triggers!
Logged
Fatories
LALE Creator
Administrator
Full Member
*****
Offline Offline

Posts: 223



View Profile
« Reply #5 on: October 23, 2010, 12:27:40 AM »

GREAT news! This may lead to being able to edit almost every interaction if my theories and tests are correct! No more being stuck with existing events! It seems that every type 2 (and other) interaction uses the scripting engine, so with a little emulation, ZOSE might be able to decompile/edit the scripts of regular interactions like people and triggers!

That is EXACTLY what I want to here Lin!  Smiley Smiley
Your theories/tests better work, or you'll crush a boys dreams.
Logged

The force of a true man can penetrate all. Nothing is safe.
Lin
ZOLE Creator
Administrator
Hero Member
*****
Online Online

Posts: 580



View Profile
« Reply #6 on: October 23, 2010, 03:39:13 AM »

Making great progress:

Logged
Lin
ZOLE Creator
Administrator
Hero Member
*****
Online Online

Posts: 580



View Profile
« Reply #7 on: October 23, 2010, 11:40:35 AM »

Making more very good progress. I am in the process of writing a small emulator that just emulates code until the scripting procedure is called, where the script address would have been loaded. It works so far. Here's the emulator's code:

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

namespace ZOSE
{
public class Emulator
{
GBHL.GBFile gb;
Stack<int> stack = new Stack<int>(); //An int because we push the PC onto here which is GBFile.BufferLocation (integer)
byte a, b, c, d, e, f, h, l;
byte bank;
bool zero, carry;
int final = -1;
byte[] ram; //Note: No bank support!

private ushort hl
{
get { return (ushort)(h * 0x100 + l); }
set { h = (byte)(value >> 8); l = (byte)(value & 0xFF); }
}

private ushort af
{
get { return (ushort)(a * 0x100 + f); }
set { a = (byte)(value >> 8); f = (byte)(value & 0xFF); }
}

private ushort bc
{
get { return (ushort)(b * 0x100 + c); }
set { b = (byte)(value >> 8); c = (byte)(value & 0xFF); }
}

private ushort de
{
get { return (ushort)(d * 0x100 + e); }
set { d = (byte)(value >> 8); e = (byte)(value & 0xFF); }
}

public Emulator(GBHL.GBFile g)
{
gb = g;
}

public int EmulateUntilScript(int id, int x, int y)
{
//Some initialization
this.a = b = c = d = e = f = h = l = 0;
//Because this isn't a REAL emulator, we'll set D to... D0
d = 0xD0;
zero = carry = false;
gb.BufferLocation = 0;
ram = new byte[0x10000];
stack = new Stack<int>();
for (int i = 0; i < 0x4000; i++)
ram[i] = gb.ReadByte();
for (int i = 0; i < 0x10; i++)
{
ram[0xD040 + i * 0x100] = 1; //We'll say we have all 16 interactions active
ram[0xD041 + i * 0x100] = (byte)(id >> 8);
ram[0xD042 + i * 0x100] = (byte)id;
ram[0xD04B + i * 0x100] = (byte)y;
ram[0xD04D + i * 0x100] = (byte)x;
//ram[0xD044 + i * 0x100] = 1; //We'll say we have all 16 interactions active
}
for (int i = 0; i < 0x20; i++)
stack.Push(0); //Emulate things for a bit
ram[0xCD00] = 1;
ram[0xC6D2] = 0x2F;
final = -1;
//Calculate the interaction's assembly address
byte first = (byte)(id >> 8);
byte second = (byte)(id);
byte a = first;
byte bank = 0;
if (a < 0x3E) bank = 08;
else if (a < 0x67) bank = 09;
else if (a < 0x98) bank = 0x0A;
else if (a < 0xDC) bank = 0x0B;
else bank = 0x0C;
int pointer = 0x3B8B + (a * 2);
gb.BufferLocation = pointer;
gb.BufferLocation = bank * 0x4000 + gb.ReadByte() + ((gb.ReadByte() - 0x40) * 0x100);

this.bank = bank;
Emulate(gb.BufferLocation);

if (final == -1)
return -1;

return 0x30000 + (final - 0x4000);
}

public int GetAddress()
{
int pointer = gb.ReadByte() + gb.ReadByte() * 0x100;
if (pointer < 0x4000 || pointer > 0x7FFF)
return pointer;
return bank * 0x4000 + pointer - 0x4000;
}

public int Emulate(int address)
{
int retCount = 0;

gb.BufferLocation = address;
byte blank, before;

while (gb.BufferLocation != 0x2518 && retCount != -1)
{
if (final != -1)
return final;
byte opcode = gb.ReadByte();
switch (opcode)
{

case 00: //NOP
break;

case 01: //LD BC,nn
c = gb.ReadByte();
b = gb.ReadByte();
break;

case 05: //DEC B
before = b;
b = (byte)(b - 1);
if (b > before)
carry = true;
else
carry = false;
if (b == 0)
zero = true;
else
zero = false;
break;

case 06: //LD B,n
b = gb.ReadByte();
break;

case 07: //RLCA
a = RotateLeft(a);
if ((a & 1) != 0)
carry = true;
else
carry = false;
if (a == 0)
zero = true;
else
zero = false;
break;

case 09: //ADD HL,BC
hl = (ushort)(hl + bc);
break;

case 0xA: //LD A,(BC)
a = ram[bc];
break;

case 0x0E: //LD C,n
c = gb.ReadByte();
break;

case 0x12: //LD (DE),A
ram[de] = a;
break;

case 0x18: //JR n
sbyte s = (sbyte)gb.ReadByte();
gb.BufferLocation += s;
break;

case 0x1A: //LD A,(DE)
a = ReadByte(de);
break;

case 0x1C: //INC E
before = e;
e = (byte)(e + 1);
if (e < before)
carry = true;
else
carry = false;
if (l == 0)
zero = true;
else
zero = false;
break;

case 0x1E: //LD E,n
e = gb.ReadByte();
break;

case 0x20: //JR NZ,n
blank = gb.ReadByte();
if (!zero)
{
gb.BufferLocation += blank;
}
break;

case 0x21: //LD HL,nn
l = gb.ReadByte();
h = gb.ReadByte();
break;

case 0x22: //LDI (HL),A
ram[hl++] = a;
break;

case 0x24: //INC H
before = h;
h = (byte)(h + 1);
if (h < before)
carry = true;
else
carry = false;
if (h == 0)
zero = true;
else
zero = false;
break;

case 0x28: //JR Z,n
blank = gb.ReadByte();
if (zero)
{
gb.BufferLocation += blank;
}
break;

case 0x2A: //LDI A,(HL)
a = ReadByte(hl++);
break;

case 0x2C: //INC L
before = l;
l = (byte)(l + 1);
if (l < before)
carry = true;
else
carry = false;
if (l == 0)
zero = true;
else
zero = false;
break;

case 0x2E: //LD L,n
l = gb.ReadByte();
break;

case 0x30: //JR NC,n
blank = gb.ReadByte();
if (!carry)
{
gb.BufferLocation += blank;
}
break;

case 0x35: //DEC (HL)
before = ram[hl];
ram[hl] = (byte)(ram[hl] - 1);
if (ram[hl] > before)
carry = true;
else
carry = false;
if (ram[hl] == 0)
zero = true;
else
zero = false;
break;

case 0x38: //JR C,n
blank = gb.ReadByte();
if (carry)
{
gb.BufferLocation += blank;
}
break;

case 0x3C: //INC A
before = a;
a = (byte)(a + 1);
if (a < before)
carry = true;
else
carry = false;
if (l == 0)
zero = true;
else
zero = false;
break;

case 0x3E: //LD A,n
a = gb.ReadByte();
break;

case 0x44: //LD B,H
b = h;
break;

case 0x47: //LD B,A
b = a;
break;

case 0x4F: //LD C,A
c = a;
break;

case 0x62: //LD H,D
h = d;
break;

case 0x66: //LD H,(HL)
h = ReadByte(hl);
break;

case 0x6F: //LD L,A
l = a;
break;

case 0x70: //LD (HL),B
ram[hl] = b;
break;

case 0x71: //LD (HL),C
ram[hl] = c;
break;

case 0x77: //LD (HL),A
ram[hl] = a;
break;

case 0x78: //LD A,B
a = b;
break;

case 0x79: //LD A,C
a = c;
break;

case 0x7C: //LD A,H
a = h;
break;

case 0x7D: //LD A,L
a = l;
break;

case 0x7E: //LD A,(HL)
a = ReadByte(hl);
break;

case 0x81: //ADD C
before = a;
a = (byte)(a + c);
if (a < before || (a == before && c != 0))
carry = true;
else
carry = false;
if (a == 0)
zero = true;
else
zero = false;
break;

case 0x85: //ADD L
before = a;
a = (byte)(a + l);
if (a < before || (a == before && l != 0))
carry = true;
else
carry = false;
if (a == 0)
zero = true;
else
zero = false;
break;

case 0x87: //ADD A
before = a;
a = (byte)(a + a);
if (a < before || (a == before && before != 0))
carry = true;
else
carry = false;
if (a == 0)
zero = true;
else
zero = false;
break;

case 0x91: //SUB C
before = a;
a = (byte)(a - c);
if (a > before || (a == before && c != 0))
carry = true;
else
carry = false;
if (a == 0)
zero = true;
else
zero = false;
break;

case 0xA6: //AND (HL)
a = (byte)(a & ram[hl]);
if (a == 0)
zero = true;
else
zero = false;
break;

case 0xAF: //XOR A
a = (byte)(a ^ a);
if (a == 0)
zero = true;
else
zero = false;
break;

case 0xB7: //OR A
a = (byte)(a | a);
if (a == 0)
zero = true;
else
zero = true;
break;

case 0xB8: //CP B
if (a == b)
zero = true;
else
zero = false;
break;

case 0xC0: //RET NZ
if (!zero)
{
retCount--;
gb.BufferLocation = PopPC();
break;
}
break;

case 0xC1: //POP BC
bc = (ushort)stack.Pop();
break;

case 0xC2: //JP NZ,nn
int addr = gb.ReadByte() + gb.ReadByte() * 0x100;
if (!zero)
{
gb.BufferLocation = CalculatePC(addr);
}
break;

case 0xC3: //JP nn
gb.BufferLocation = CalculatePC(gb.ReadByte() + gb.ReadByte() * 0x100);
break;

case 0xC5: //PUSH BC
stack.Push(bc);
break;

case 0xC7: //RST 00
PushPC();
Emulate(0);
break;

case 0xC8: //RET Z
if (zero)
{
retCount--;
gb.BufferLocation = PopPC();
break;
}
break;

case 0xC9: //RET
retCount--;
gb.BufferLocation = PopPC();
break;

case 0xCB: //VARYING
switch (gb.ReadByte())
{
case 0x37: //SWAP A
a = (byte)(((a & 0xF) << 4) + (a >> 4));
break;
}
break;

case 0xCD: //Call
address = GetAddress();
PushPC();
if (address == 0x2518)
{
final = hl;
return final;
}
if (address == 0x2544)
{
final = hl;
return final;
}
int i = Emulate(address);
/*if (address == 0x2518)
{
final = hl;
return final;
}
gb.BufferLocation = PopPC();*/
break;

case 0xD1: //POP DE
de = (ushort)stack.Pop();
break;

case 0xD9: //RETI
retCount--;
gb.BufferLocation = PopPC();
break;

case 0xDF: //RST 18
PushPC();
Emulate(0x18);
break;

case 0xE1: //POP HL
hl = (ushort)stack.Pop();
break;

case 0xE5: //PUSH HL
stack.Push(hl);
break;

case 0xE6: //AND A,n
a &= gb.ReadByte();
break;

case 0xE9: //JP HL
gb.BufferLocation = CalculatePC(hl);
break;

case 0xEF: //RST 28
PushPC();
Emulate(0x28);
break;

case 0xF0: //LD A,(FF00+n)
a = ram[0xFF00 + gb.ReadByte()];
break;

case 0xF1: //POP AF
af = (ushort)stack.Pop();
break;

case 0xF6: //OR A,n
a = (byte)(a | gb.ReadByte());
break;

case 0xFA: //LD A,(nn)
a = ReadByte(GetAddress());
break;

case 0xFE: //CP A,n
if (a == gb.ReadByte())
zero = true;
else
zero = false;
break;

default:
opcode = (byte)(opcode - 0);
break;
}
}

if (retCount == -1)
return -1;
return gb.BufferLocation;
}

public byte RotateLeft(byte b)
{
byte carry = (byte)(b >> 7);
return (byte)((b << 1) | carry);
}

public byte ReadByte(int address)
{
if (address < 0x4000 || address > 0x7FFF)
return ram[address];
return gb.Buffer[bank * 0x4000 + address - 0x4000];
}

public int CalculatePC(int address)
{
if (address < 0x4000)
return address;
return bank * 0x4000 + address - 0x4000;
}

public void PushPC()
{
int address = gb.BufferLocation;
if (address < 0x4000)
{
stack.Push(address);
return;
}
address = address % 0x4000;
address += 0x4000;
stack.Push(address);
}

public int PopPC()
{
int address = stack.Pop();
if (address < 0x4000)
return address;
address += bank * 0x4000 - 0x4000;
return address;
}
}
}

Beautiful, isn't it? Because of that, we'll be able view the scripts of countless more interactions and increase the flexibility of Oracles editing by loads.
Logged
Lin
ZOLE Creator
Administrator
Hero Member
*****
Online Online

Posts: 580



View Profile
« Reply #8 on: October 24, 2010, 12:00:06 AM »

Well, I've decided to give up on the emulator. There are too many things to worry about and it's too much work to test and work on. So, instead I'll be adding custom interactions and a patch in ZOLE that will allow the scripting of either any interaction or my own interaction ID. Sorry.
Logged
Lin
ZOLE Creator
Administrator
Hero Member
*****
Online Online

Posts: 580



View Profile
« Reply #9 on: October 24, 2010, 10:59:52 PM »

Good news. I have overwrote opcode FC, which was the final delay-setting opcode, and made it a 3-byte jump opcode. What happens is it reads from where you tell it to read and writes the bytes in the memory (At C300 since it seems vacant). Then it emulates the data there as normal, so now instead of being with limited space, we have the whole ROM to do as we please. However, we still need to use bank 0C to initialize things, so as soon as we set the write location, we do jump and set the write location to something else!
Logged
Lin
ZOLE Creator
Administrator
Hero Member
*****
Online Online

Posts: 580



View Profile
« Reply #10 on: October 25, 2010, 06:18:13 AM »

Great news. Following up on the opcode FC change, I rewrote some stuff in ZOSE to allow creating more than one script at once, and later decompile one and decompile whatever it jumps to. In other news, I have created interaction 72 (I destroyed the old one and created my own) which is basically like interaction 20, except the scripts can be used from anywhere, including overworlds and dungeons. This means we can now use ZOSE to create any scripting event we want anywhere, which means hacks will become much better! Here's all it took:

Code:
gb.WriteBytes(0x2BE09, new byte[] { 0x1E, 0x42, 0x21, 0x19, 0x7E, 0x1A, 0xDF, 0x2A, 0x66, 0x6F, 0xCD, 0x44, 0x25, 0xC3, 0x52, 0x25 });
gb.WriteByte(0x3C6F, 0x09);
gb.WriteByte(0x3C70, 0x7E);

This new script allows 243 scripts to be used (Sorry it's not 256. There wasn't enough space in bank 0xA). The only problem we face is the space we're left with in bank 0C, but each script we create will only need 6 bytes there. Pretty neat, huh?
Logged
m64m
Newcomer
*
Offline Offline

Posts: 6



View Profile
« Reply #11 on: October 26, 2010, 04:06:51 PM »

This sounds totally awesome. Great work =D
Logged
Lin
ZOLE Creator
Administrator
Hero Member
*****
Online Online

Posts: 580



View Profile
« Reply #12 on: October 28, 2010, 05:30:53 AM »

Okay. Although I haven't worked on ZOSE too much due to being sick, I did manage to fix all the interaction 72 and jump3byte (opcode FC) bugs. This is all it takes to make a chest appear with a puff and sound effect when you've killed all enemies:

Logged
Supreme Dirt
Jr. Member
**
Offline Offline

Posts: 64


View Profile
« Reply #13 on: October 30, 2010, 11:45:01 PM »

How feasible will it be to make enemies do things, like say an enemy moving a pot in a boss battle? Will it be possible to change enemy and boss AI with this?
Logged
Lin
ZOLE Creator
Administrator
Hero Member
*****
Online Online

Posts: 580



View Profile
« Reply #14 on: October 30, 2010, 11:46:28 PM »

No we won't. I'm pretty sure that's all assembly, but you never know. If there's anyone who would know the answer to that question, it's Jigglysaint. Even if we did know how the AI works, just remember, it's even hard in regular games so it'll surely be hard in these.
Logged
Pages: [1] 2
  Print  
 
Jump to:  

Powered by MySQL Powered by PHP Powered by SMF 1.1.19 | SMF © 2013, Simple Machines Valid XHTML 1.0! Valid CSS!