Lin
|
 |
« 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.0Welcome 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. 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
|
 |
« 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
|
 |
« 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
|
 |
« Reply #3 on: October 22, 2010, 02:49:51 AM » |
|
Big update: Decompiling (and also compiling), by interaction ID or address: 
|
|
|
Logged
|
|
|
|
Lin
|
 |
« 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
|
 |
« 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!  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
|
 |
« Reply #6 on: October 23, 2010, 03:39:13 AM » |
|
Making great progress: 
|
|
|
Logged
|
|
|
|
Lin
|
 |
« 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: 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
|
 |
« 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
|
 |
« 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
|
 |
« 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: 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
Posts: 6
|
 |
« Reply #11 on: October 26, 2010, 04:06:51 PM » |
|
This sounds totally awesome. Great work =D
|
|
|
Logged
|
|
|
|
Lin
|
 |
« 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
|
 |
« 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
|
 |
« 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
|
|
|
|
|