Browse Source

Initial commit

master
mid-kid 5 years ago
commit
d8fbf80a4b
  1. 2
      README.md
  2. 98
      pk3/calc.c
  3. 38
      pk3/calc.py
  4. 222
      pk3/fools.asm
  5. BIN
      pk3/output.buf
  6. 86
      pk3/pk3_5seconds.c
  7. 29
      pk4/dumprop.py
  8. 78
      pk4/dumpropscript.py
  9. 997
      pk4/fools.asm
  10. BIN
      pk4/fools.sav
  11. 30
      pk4/hook.asm
  12. BIN
      pk4/orig.sav
  13. 41
      pk4/ropgadgets.txt
  14. 196
      pk4/ropscript.c
  15. 72
      pokecrystal.diff

2
README.md

@ -0,0 +1,2 @@
My resources used to beat https://zzazzdzz.github.io/fools2019/
No writeups or explanations provided.

98
pk3/calc.c

@ -0,0 +1,98 @@
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <glib.h>
int s03_A0F3 = 0x5D0B1C11;
int s03_A0F7 = 0x35E79125;
int s03_A0FB = 0x56596b10;
int s03_A0FF = 0x7FFFFB0A;
int s03_A103 = 0x1B080733;
int s03_A107;
int s03_A10B;
int s03_A10D;
int s03_A10E;
char buffer[0x200];
int main()
{
FILE *f = fopen("fools.sav", "r");
if (!f) {
perror("fopen");
exit(1);
}
fseek(f, 0x6567, SEEK_SET);
fread(buffer, 1, 0x200, f);
fclose(f);
GHashTable *hashtable = g_hash_table_new(g_direct_hash, g_direct_equal);
double start = clock();
do {
gint lcg = s03_A0F3 & 0xFFFFFE00;
gint cur_lcg = GPOINTER_TO_INT(g_hash_table_lookup(hashtable, GINT_TO_POINTER(lcg)));
cur_lcg += 1;
g_hash_table_replace(hashtable, GINT_TO_POINTER(lcg), GINT_TO_POINTER(cur_lcg));
s03_A0F3 = (s03_A0F3 * s03_A0F7 + s03_A0FB) & 0xFFFFFFFF;
if (s03_A0FF % 0x100000 == 0) {
double framespers = 0x100000 / ((double)(clock() - start) / CLOCKS_PER_SEC);
unsigned eta = (double)(s03_A0FF) / framespers;
fprintf(stderr, "Progress: %d (speed: %.2f iter/s, ETA: %02u:%02u:%02u)\n",
s03_A0FF, framespers,
eta / 60 / 60, (eta / 60) % 60, eta % 60);
start = clock();
}
} while (--s03_A0FF);
int count = 0;
GHashTableIter iter;
gpointer key, value;
g_hash_table_iter_init(&iter, hashtable);
while (g_hash_table_iter_next(&iter, &key, &value)) {
count += GPOINTER_TO_INT(value) % 2;
}
int i = 0;
start = clock();
g_hash_table_iter_init(&iter, hashtable);
while (g_hash_table_iter_next(&iter, &key, &value)) {
if (GPOINTER_TO_INT(value) % 2 == 0) {
continue;
}
double framespers = 1 / ((double)(clock() - start) / CLOCKS_PER_SEC);
unsigned eta = (double)(count - i) / framespers;
fprintf(stderr, "Progress: %d/%d %.2f%% (speed: %.2f iter/s, ETA: %02u:%02u:%02u)\n",
i, count, (double)i / count * 100, framespers,
eta / 60 / 60, (eta / 60) % 60, eta % 60);
start = clock();
i += 1;
s03_A107 = s03_A103;
s03_A10B = (GPOINTER_TO_INT(key) & 0xFF00) >> 8;
s03_A10D = (GPOINTER_TO_INT(key) & 0xFF0000) >> 16;
s03_A10E = (GPOINTER_TO_INT(key) & 0xFF000000) >> 24;
int bufi = 0;
do {
s03_A10B = ((s03_A10B / 2) * s03_A10D + s03_A10E) & 0xFFFF;
buffer[bufi] ^= s03_A10B & 0xFF;
if (++bufi >= 0x200) bufi = 0;
} while (--s03_A107);
}
g_hash_table_destroy(hashtable);
f = fopen("output.buf", "w");
fwrite(buffer, 1, 0x200, f);
fclose(f);
return 0;
}

38
pk3/calc.py

@ -0,0 +1,38 @@
# Seed values (break at s03_A13A to fetch them)
s03_A0F3 = 0x5D0B1C11 # Match
s03_A0F7 = 0x35E79125 # Match
s03_A0FB = 0x56596b10 # Match
s03_A0FF = 0x7FFFFB0A # Match
s03_A103 = 0x1b080733 # Match
file = open("fools.sav", "rb")
file.seek(0x6567)
buf = bytearray(file.read(0x200))
file.close()
while s03_A0FF != 0:
s03_A107 = s03_A103
s03_A10B = (s03_A0F3 & 0xFF00) >> 8
s03_A10D = (s03_A0F3 & 0xFF0000) >> 16
s03_A10E = (s03_A0F3 & 0xFF000000) >> 24
print(s03_A0FF - i_out)
bufi = 0
i = 0
while s03_A107 != 0:
s03_A10B = ((s03_A10B // 2) * s03_A10D + s03_A10E) & 0xFFFF
buf[bufi] ^= (s03_A10B) & 0xFF
bufi += 1
if bufi >= 0x200:
bufi = 0
s03_A107 -= 1
s03_A0F3 = (s03_A0F3 * s03_A0F7 + s03_A0FB) & 0xFFFFFFFF
s03_A0FF -= 1
file = open("output.buffer", "wb").write(buf)

222
pk3/fools.asm

@ -0,0 +1,222 @@
CopyDEtoHL_32bit: ; s03_A084
; Copies value pointed to by de to hl
ld a, [de]
ld [hl+], a
inc de
ld a, [de]
ld [hl+], a
inc de
ld a, [de]
ld [hl+], a
inc de
ld a, [de]
ld [hl+], a
ret
AddDEToHL_32bit: ; s03_A090
; Adds value pointed to by de to value pointed to by hl
ld a, [de]
add [hl]
ld [hl+], a
inc de
ld a, [de]
adc [hl]
ld [hl+], a
inc de
ld a, [de]
adc [hl]
ld [hl+], a
inc de
ld a, [de]
adc [hl]
ld [hl], a
ret
s03_A0A0: dl $FFFFFFFF
DecHL_32bit: ; s03_A0A4
; Decrements 32-bit value at hl, checks if result is 0
push hl
ld de, s03_A0A0
call AddDEToHL_32bit
pop hl
; Checks 4-byte value at hl is 0
ld a, [hl+]
and a
ret nz
ld a, [hl+]
and a
ret nz
ld a, [hl+]
and a
ret nz
ld a, [hl+]
and a
ret
Multiplier: ds 4 ; s03_A0B8
CalcResult: ds 4 ; s03_A0BC
Clean32bit: dl $00000000 ; s03_A0C0
MulDEToHL_32bit: ; s03_A0C4
; Multiplies value at de with value at hl, stores in hl
push hl
; Copy DE to the Multiplier
push hl
push de
ld hl, Multiplier
call CopyDEtoHL_32bit
pop de
pop hl
; Clear the result value
push hl
ld hl, CalcResult
ld de, Clean32bit
call CopyDEtoHL_32bit
pop de
; Add HL (now DE) Multiplier times to CalcResult
.loop
push de
ld hl, CalcResult
call AddDEToHL_32bit
pop de
push de
ld hl, Multiplier
call DecHL_32bit
pop de
jr nz, .loop
; Copy the result back to HL
pop hl
ld de, CalcResult
jp CopyDEtoHL_32bit
s03_A0F3: ds 4 ; Initial value: $5D0B1C11
s03_A0F7: dl $35E79125
s03_A0FB: dl $56596b10
s03_A0FF: ds 4 ; Initial value: $7FFFFB0A
s03_A103: dl $1b080733
s03_A107: ds 4 ; Initialized with A103
s03_A10B: dw
s03_A10D: db
s03_A10E: db
s03_A10F:
; (u16)s03_A10B = ((u16)s03_A10B / 2) * s03_A10D + s03_A10E
ld hl, s03_A10B
ld a, [hl+]
ld h, [hl]
ld l, a
and a
srl h
rr l
ld d, h
ld e, l
ld hl, 0
ld a, [s03_A10D]
ld c, a
and a
jr z, .end
.loop
add hl, de
dec c
jr nz, .loop
.end
ld a, [s03_A10E]
ld c, a
ld b, 0
add hl, bc
ld a, h
ld [s03_A10B + 1], a
ld a, l
ld [s03_A10B], a
ret
s03_A13A:
; Main decryption routine
.outer_loop
ld hl, s03_A107
ld de, s03_A103
call CopyDEtoHL_32bit
; Initialize values
ld hl, s03_A0F3 + 1
ld de, s03_A10B
ld a, [hl+]
ld [de], a
inc de ; s03_A10B + 1
xor a
ld [de], a
inc de ; s03_A10D
ld a, [hl+]
ld [de], a
inc de ; s03_A10E
ld a, [hl]
ld [de], a
ld hl, Buffer
ld bc, 0
; Problem with this routine: Writing to SRAM!!!
; This means that whatever is in hl's buffer is useless on next bootup, so we
; need to make sure to start from a clean save file.
.loop ; s03_A15A
push hl
push bc
call s03_A10F ; (u16)s03_A10B = ((u16)s03_A10B / 2) * s03_A10D + s03_A10E
pop bc
pop hl
; Exor the buffer
xor [hl]
ld [hl+], a
; Reset HL to Buffer every $200 bytes
inc bc
ld a, b
cp $2
jr nz, .s03_A16F
ld bc, 0
ld hl, Buffer
.s03_A16F
push hl
ld hl, s03_A107
call DecHL_32bit
pop hl
jr nz, .loop
ld hl, s03_A0F3
ld de, s03_A0F7
call MulDEToHL_32bit
ld hl, s03_A0F3
ld de, s03_A0FB
call AddDEToHL_32bit
ld hl, s03_A0FF
call DecHL_32bit
jr nz, .outer_loop
ret
Buffer: ds $200 ; s03_A567

BIN
pk3/output.buf

Binary file not shown.

86
pk3/pk3_5seconds.c

@ -0,0 +1,86 @@
// Written by luckytyphlosion
#include <stdio.h>
#include <time.h>
#include <string.h>
typedef unsigned int uint;
typedef unsigned short u16;
typedef unsigned char u8;
#define _A0F4 (_A0F3 >> 8) & 0xff
#define _A0F5 (_A0F3 >> 16) & 0xff
#define _A0F6 (_A0F3 >> 24) & 0xff
#define TRUE 1
#define FALSE 0
u8 sA567[] = {0x15, 0xc8, 0x7b, 0x6b, 0x7f, 0xa1, 0xa3, 0x29, 0xe8, 0x81, 0x1, 0x99, 0x9f, 0xeb, 0xf2, 0x80, 0x5f, 0xa3, 0x96, 0x7c, 0x17, 0xdb, 0xd2, 0x41, 0xa1, 0xbc, 0xbb, 0x13, 0xbc, 0xd8, 0xc3, 0x9, 0xf4, 0x2, 0x1b, 0x95, 0xf3, 0x72, 0x65, 0xdb, 0x6f, 0x82, 0x9e, 0x52, 0xd9, 0x89, 0xd8, 0xdf, 0x5a, 0xb0, 0x3, 0xf2, 0x9a, 0xe6, 0xef, 0x81, 0x32, 0xa5, 0x57, 0x60, 0xe0, 0xc8, 0x2d, 0x8c, 0x52, 0x63, 0x5d, 0xd8, 0xf, 0x72, 0xdf, 0xa1, 0x36, 0x1c, 0x3e, 0xc8, 0xc2, 0x4f, 0x1e, 0xfd, 0x1d, 0x11, 0xc, 0x74, 0x13, 0x21, 0x49, 0xad, 0xc0, 0xd9, 0xba, 0xda, 0xd, 0xe4, 0x6, 0x8e, 0x94, 0xe6, 0x25, 0x8b, 0xba, 0xc2, 0x2b, 0x92, 0xa, 0x90, 0xa1, 0x4a, 0x6, 0x53, 0x84, 0x79, 0xe5, 0xe7, 0xcf, 0x60, 0x74, 0xd2, 0x3b, 0x2a, 0xe0, 0x56, 0x32, 0xa6, 0x10, 0x69, 0x52, 0xf, 0xd3, 0x3d, 0x7f, 0xf3, 0x12, 0x6d, 0x0, 0x57, 0x41, 0x3f, 0x96, 0x3b, 0x8, 0xb7, 0x48, 0x72, 0xb2, 0x1e, 0x12, 0x57, 0x60, 0x95, 0x23, 0xc8, 0xab, 0x52, 0xc8, 0x3f, 0xc4, 0xc0, 0x50, 0xc1, 0xd3, 0x38, 0x28, 0x6e, 0x96, 0x6d, 0xdf, 0x8f, 0x41, 0x24, 0x21, 0x91, 0xf4, 0x64, 0x3e, 0xc9, 0xe6, 0xc7, 0x45, 0x26, 0xa1, 0x4c, 0xa, 0xd3, 0x2f, 0x8f, 0x77, 0x7d, 0x68, 0x86, 0x23, 0x43, 0xcf, 0xbf, 0xe1, 0xf4, 0xc8, 0x9e, 0x35, 0x5a, 0x11, 0x26, 0xb7, 0x9f, 0x23, 0xcd, 0x68, 0xa2, 0xb5, 0xca, 0xd3, 0x53, 0x95, 0xda, 0xb, 0x87, 0x15, 0xca, 0xe7, 0x73, 0xbd, 0x66, 0x39, 0x90, 0xc0, 0xa4, 0x94, 0x94, 0x3e, 0x61, 0x9e, 0xef, 0x89, 0x1e, 0x93, 0x13, 0xc1, 0xfa, 0xd9, 0xee, 0x5b, 0x27, 0x71, 0x16, 0xcd, 0xa2, 0xef, 0x49, 0xcc, 0xd6, 0x1d, 0x24, 0xa6, 0x3b, 0x89, 0x94, 0x58, 0x28, 0xd2, 0x83, 0x5b, 0x25, 0x8a, 0x5f, 0x25, 0xfe, 0xe3, 0x2d, 0xe8, 0x58, 0x5e, 0xbb, 0x9b, 0xd5, 0xe8, 0x46, 0xa5, 0xdc, 0xde, 0x8f, 0x97, 0x3, 0xf1, 0x16, 0x1, 0xfc, 0x2e, 0x3f, 0x47, 0x5b, 0xaf, 0x71, 0x2e, 0x7f, 0x19, 0xce, 0xe3, 0x7d, 0xe2, 0x49, 0x5e, 0x71, 0xa, 0x57, 0xe1, 0x98, 0xac, 0xdd, 0x7f, 0xfd, 0x5d, 0xae, 0xdc, 0x24, 0xc5, 0x30, 0x3b, 0x65, 0xc1, 0xd6, 0xf4, 0x62, 0xa0, 0xee, 0xbc, 0x86, 0x68, 0xb4, 0x5, 0x68, 0x1b, 0xdb, 0x5c, 0xa8, 0x25, 0x92, 0x70, 0xc8, 0xa5, 0xf6, 0x1a, 0x55, 0xad, 0xae, 0x44, 0x2a, 0x4a, 0x77, 0xca, 0x2d, 0x6b, 0xdd, 0x47, 0x51, 0x95, 0x58, 0xff, 0x75, 0xdd, 0x30, 0x81, 0x80, 0x1b, 0xdb, 0xc2, 0xa9, 0x29, 0xce, 0xa8, 0xd6, 0xd8, 0x8, 0xc1, 0x86, 0x1c, 0xec, 0xf, 0x63, 0x48, 0x38, 0x1b, 0xeb, 0x32, 0xe3, 0xa0, 0xe6, 0x28, 0x5e, 0xf4, 0xa, 0xa2, 0x2d, 0xf1, 0xb0, 0x61, 0xfc, 0xdd, 0x4c, 0x6b, 0x43, 0xe8, 0x82, 0x4, 0xe8, 0xf3, 0x3d, 0x43, 0xf5, 0x6d, 0x4e, 0x8a, 0x45, 0xc9, 0x6a, 0x96, 0x1d, 0x37, 0x93, 0x76, 0x53, 0x9e, 0x33, 0xa, 0xaf, 0x8, 0x3e, 0x80, 0xfc, 0xe7, 0xc5, 0x17, 0x69, 0xe7, 0x91, 0x55, 0x52, 0x4a, 0xa5, 0x51, 0x6, 0xa8, 0x3e, 0x82, 0x1d, 0xff, 0x27, 0x14, 0xee, 0x26, 0x39, 0x55, 0xc0, 0x8f, 0xd7, 0xf2, 0x47, 0x14, 0x9a, 0x54, 0x5e, 0xac, 0x9a, 0xc2, 0x93, 0xc0, 0x96, 0x60, 0x44, 0x11, 0x8, 0xed, 0x22, 0x20, 0x44, 0x77, 0xb3, 0xfc, 0x78, 0xe5, 0xda, 0xa, 0xc5, 0xa1, 0xbe, 0x64, 0x80, 0x25, 0x20, 0xa7, 0xfb, 0x3e, 0xdb, 0x26, 0x39, 0x99, 0x2a, 0xee, 0xa3, 0x36, 0x7b, 0xb6, 0x63, 0x24, 0xf6, 0xf6, 0x13, 0xe, 0x83, 0x86, 0x1f, 0x3a, 0xf};
u16 values[65536] = {0};
u16 values_set[65536] = {0};
int values_size = 0;
int main(void) {
uint _A0F3 = 0x5d0b1c11;
const uint _A0F7 = 0x35e79125;
const uint _A0FB = 0x56596b10;
struct timespec start_time;
for (int i = 0; i < 0x3ffffb0a; i++) {
_A0F3 = _A0F3 * _A0F7 + _A0FB;
}
for (int i = 0; i < 0x4f6; i++) {
u16 _A10B = _A0F4;
int _A10D = _A0F5;
int _A10E = _A0F6;
int data_count = 0;
int start_index = -1;
values_size = 0;
int sublcg_count = 0;
memset(values_set, 0xff, sizeof(values_set));
while (TRUE) {
values[values_size++] = _A10B;
_A10B = (_A10B >> 1) * _A10D + _A10E;
if (values_set[_A10B] != 0xffff) {
start_index = values_set[_A10B];
break;
}
values_set[_A10B] = values_size;
sA567[data_count++] ^= (_A10B & 0xff);
data_count = data_count & 0x1ff;
sublcg_count++;
}
int sublcg_index = start_index;
int period = values_size - start_index;
while ((period & 1) == 0) {
period >>= 1;
}
int num_xors_until_repeat = period * 512;
int num_repeat_iterations = (0x1b080733 - sublcg_count) / num_xors_until_repeat;
int remaining_sublcg_iterations = (0x1b080733 - sublcg_count) % num_xors_until_repeat;
if (num_repeat_iterations & 1 == 1) {
remaining_sublcg_iterations += num_xors_until_repeat;
}
for (int j = 0; j < remaining_sublcg_iterations; j++) {
sA567[data_count++] ^= (values[sublcg_index++] & 0xff);
data_count = data_count & 0x1ff;
if (sublcg_index >= values_size) {
sublcg_index = start_index;
}
}
_A0F3 = _A0F3 * _A0F7 + _A0FB;
if (i % 10000 == 9999) {
struct timespec end_time;
clock_gettime(CLOCK_MONOTONIC, &end_time);
fprintf(stderr, "difftime: %f, iter: %u\n", 1000.0*end_time.tv_sec + 1e-6*end_time.tv_nsec - (1000.0*start_time.tv_sec + 1e-6*start_time.tv_nsec), i);
}
}
for (int i = 0; i < 0x200; i++) {
printf("0x%02x, ", sA567[i]);
}
}

29
pk4/dumprop.py

@ -0,0 +1,29 @@
#!/usr/bin/env python3
from sys import argv
from struct import unpack
gadgets = {}
for line in open("ropgadgets.txt"):
split = line.split("-")
if len(split) < 2:
continue
addr = int(split[0][1:], 16)
cmd = "-".join(split[1:]).strip()
gadgets[addr] = cmd
mem = open("fools.dump", "rb").read()
addr = int(argv[1], 0)
while True:
gadget = unpack("<H", mem[addr:addr+2])[0]
if gadget in gadgets:
print(" ", gadgets[gadget])
else:
print(" dw $%04X" % gadget)
if gadget == 0x1708:
break
addr += 2

78
pk4/dumpropscript.py

@ -0,0 +1,78 @@
#!/usr/bin/env python3
from sys import argv
from struct import unpack
mem = open("fools.dump", "rb").read()
addr = int(argv[1], 0)
while True:
gadget = unpack("<B", mem[addr:addr+1])[0]
if gadget == 0x01:
param = unpack("<H", mem[addr+1:addr+3])[0]
print(" ; sSaveSource = $%04X" % param)
print(" dbw $01, $%04X ; ROP_s02_AEF3" % param)
print()
addr += 3
elif gadget == 0x02:
param = unpack("<B", mem[addr+1:addr+2])[0]
print(" ; sSaveBlockChecksum = $%02X" % param)
print(" dbb $02, $%02X ; ROP_s02_AF09" % param)
print()
addr += 2
elif gadget == 0x03:
print(" ; w00_C800 = *(sSaveSource++)")
print(" db $03 ; ROP_s02_AF19")
print()
addr += 1
elif gadget == 0x05:
print(" ; w00_C800 ^= func(s02_ADB1)")
print(" db $05 ; ROP_s02_AF3B")
print()
addr += 1
elif gadget == 0x06:
print(" ; rotatebuffer(w00_C800)")
print(" db $06 ; ROP_s02_AF85")
print()
addr += 1
elif gadget == 0x07:
param = unpack("<I", mem[addr+1:addr+5])[0]
print(" ; s02_ADB1 = $%08X" % param)
print(" dbl $07, $%08X ; ROP_s02_AFE3" % param)
print()
addr += 5
elif gadget == 0x08:
print(" ; sSaveBlockChecksum += w00_C800")
print(" db $08 ; ROP_s02_B005")
print()
addr += 1
elif gadget == 0x09:
print(" ; sSaveBlockChecksum ^= w00_C800")
print(" db $09 ; ROP_s02_B013")
print()
addr += 1
elif gadget == 0x0B:
print(" ; w00_C800 = sSaveBlockChecksum")
print(" db $0B ; ROP_s02_B033")
print()
addr += 1
elif gadget == 0x0C:
param1 = unpack("<H", mem[addr+1:addr+3])[0]
param2 = unpack("<B", mem[addr+3:addr+4])[0]
print(" dbwb $0C, $%03X, $%02X ; ROP_rept" % (param1, param2))
print()
addr += 4
elif gadget == 0x0D:
print(" ; *(sSaveSource++) = sSaveBlockChecksum")
print(" db $0D ; ROP_s02_B045")
print()
addr += 1
elif gadget == 0x0E:
print(" ; sSaveBlockChecksum = *sSaveSource")
print(" db $0E ; ROP_s02_B069")
print()
addr += 1
else:
break

997
pk4/fools.asm

@ -0,0 +1,997 @@
Text_WouldYouLikeToSaveTheGame EQU $5283
Text_SavingDontTurnOffThePower EQU $5288
SaveTheGame_yesorno EQU $4BAF
SaveMenu.refused EQU $4A4A
Text_WaitBGMap EQU $13B6
SpeechTextBox EQU $103E
FarCall EQU $08
FadeOutPalettes EQU $23:$4084
LoadMapPalettes EQU $41:$47EB
ClearScreen EQU $0FDB
UpdateSprites EQU $1AD2
GetSGBLayout EQU $3340
SetPalettes EQU $32F9
DelayFrame EQU $045A
DelayFrames EQU $0468
PrintText EQU $1057
Request1bpp EQU $0F1E
DoubleSpeed EQU $2FEF
hTransferVirtualOAM EQU $FFEC
rSTAT EQU $FF41
CopyBytes EQU $3026
s02_9A12: db ; s02_9A12
SaveGame: ; s02_AD44
push af
push bc
push de
push hl
ei
call Text_WaitBGMap
ld a,[wSavingAllowed]
and a
jr z,.cantsave
; Ask the player to save or some dumb shit idfk
ld hl,$B1E8
ld a,$02
ld [w01_DF34],a
call $A2A3
ld bc,$0204
ld hl,$A41A
ld de,Start
call $AB1F ; Get player input?
and a
jr z,.declined
; Confirmed save
ld a,[wPartyCount]
cp a,$04
jr c,.actuallysave
; Too many party members yadda yadda
ld hl,$B323
ld a,$02
ld [w01_DF34],a
call $A2A3
ld bc,$0204
ld hl,$A41A
ld de,Start
call $AB1F
and a
jr z,$ADA7
.actuallysave ; s02_AD8F
ld a,BANK(FadeOutPalettes)
ld hl,FadeOutPalettes
rst FarCall
ld a,BANK(LoadMapPalettes)
ld hl,LoadMapPalettes
rst FarCall
jp SavingTheGame
.cantsave ; s02_A9DC
ld hl,$B1A6
ld a,$02
ld [$DF34],a
call $A2A3
.declined ; s02_ADA7
pop hl
pop de
pop bc
pop af
jp $0460
sSaveSource: dw ; s02_ADAE
sSaveBlockChecksum: db ; s02_ADB0
s02_ADB1: dl ; s02_ADB1
sBufferCarry: ; s02_ADB5
sROPScriptBackup: dw ; s02_ADB6
s02_ADB8: db ; s02_ADB8
s02_ADB9: db ; s02_ADB9
ROPGadget_ldi_a_hl: ; s02_ADBA
ld a, [hl+]
ret
ROPGadget_ldd_a_hl: ; s02_ADBC
ld a, [hl-]
ret
ROPGadget_ld_hl_c: ; s02_ADBE
ld [hl], c
ret
ROPGadget_ld_b_hl: ; s02_ADC0
ld b, [hl]
ret
ROPGadget_add_b: ; s02_ADC2
add b
ret
ROPGadget_srl_a: ; s02_ADC4
srl a
ret
ROPGadget_xor_hl: ; s02_ADC7
xor [hl]
ret
ROPGadget_call_z: ; s02_ADC9
ret z
pop hl
ret
ROPGadget_variable: ; s02_ADCC
add [hl]
ret
SavingTheGame: ; s02_ADCE
; Cleanup shit because well fuck nobody cares
call ClearScreen
call UpdateSprites
call UninstallVBlankHook ; Only interesting line in this entire blob of fuzz
ld b,$08
call GetSGBLayout
call SetPalettes
ld hl,wMusicFadeID
ld [hl],$00
dec hl
dec hl
ld [hl],$08
ld c,$32
call DelayFrames
ld a,$03
ld [wOptions],a
ld a,$05
rst $10
ld hl,Text_SavingDontTurnOffThePower
call PrintText
ld de,$B400
ld hl,$8C00 ; This graphic is probably a picture of a cucumber
ld bc,$0104
call Request1bpp
call DelayFrame
; Now THAT's more like it
ld de,ROPScript_SaveMain
di
call DoubleSpeed
call UpdateSaveSpinner
ld sp,SaveROPMain
ret
ROP_SaveMain: ; s02_AE18
ld a, [de]
inc de
add a
ld bc, 0
ld c, a
ld hl, .jmptable
add hl, bc
ldw hl, [hl]
.jmptable ; s02_AE2C
jp hl
dw ROP_SetAddr ; $01
dw ROP_InitChecksum ; $02
dw ROP_FetchByte ; $03
dw ROP_end ; $04
dw ROP_ApplyCrypto ; $05
dw ROP_RotateBuffer ; $06
dw ROP_InitCrypto ; $07
dw ROP_AddChecksum ; $08
dw ROP_XorChecksum ; $09
dw $0000 ; $0A
dw ROP_WriteChecksumByte ; $0B
dw ROP_rept ; $0C
dw ROP_StoreByte ; $0D
dw ROP_LoadByte ; $0E
ROP_end: ; s02_AE4A
ld bc, $0100
ld de, $CB00
ld hl, $AE58
ld af, $FF00
call CopyBytes
; Here's the destination of the copied code,
; it does some shit to copy the buffer back to the SRAM.
; I don't really care what it does.
UpdateSaveSpinner: ; s02_AEDB
ld hl,s02_9A12
ld b,$C0
ld a,[s02_ADB9]
inc a
and a,%11
ld [s02_ADB9],a
add b
ld b,a
; Wait for HBlank
.loop ; s02_AEEB
ldh a,[rSTAT]
and a,%11
jr nz,.loop
ld [hl],b
ret
ROP_SetAddr: ; s02_AEF3
; sSaveSource = param
ld hl, sSaveSource
ld a, [de]
ld [hl+], a
inc de
ld a, [de]
ld [hl+], a
inc de
ld hl, ROP_SaveMain
jp hl
ROP_InitChecksum: ; s02_AF09
; sSaveBlockChecksum = param
ld hl, sSaveBlockChecksum
ld a, [de]
inc de
ld [hl+], a
ld hl, ROP_SaveMain
jp hl
ROP_FetchByte: ; s02_AF19
; w00_C800 = *(sSaveSource++)
; Fetch byte from pointer stored at sSaveSource
ld hl, sSaveSource
ldw hl, [hl]
ld a, [hl+]
; Save incremented pointer back into sSaveSource
ld b, h
ld c, l
ld hl, sSaveSource
ld [hl], c
inc hl
ld [hl], b
; Save byte into w00_C800
ld hl, w00_C800
ld [hl+], a
ld hl, ROP_SaveMain
jp hl
ROP_ApplyCrypto: ; s02_AF3B
; w00_C800 ^= func(s02_ADB1)
; s02_ADB2 ^= (s02_ADB1 + 1) ^ s02_ADB4
ld hl, s02_ADB1
inc [hl]
ld b, [hl] ; s02_ADB1
ld a, [hl+]
ld a, [hl+]
ld a, [hl+]
ld a, [hl-] ; s02_ADB4
xor b
ld b, a
ld a, [hl-]
ld a, [hl-] ; s02_ADB2
inc hl
xor b
ld [hl+], a ; s02_ADB2
; s02_ADB3 += s02_ADB2
ld b, a
ld a, [hl-] ; s02_ADB3
inc hl
add b
ld [hl+], a ; s02_ADB3
; s02_ADB4 = s02_ADB2 ^ (s02_ADB4 + (s02_ADB3 / 2))
srl a
ld b, a
ld a, [hl-] ; s02_ADB4
add b
ld b, a
ld a, [hl-]
ld a, [hl+] ; s02_ADB2
xor b
inc hl
ld [hl+], a ; s02_ADB4
; w00_C800 ^= s02_ADB4
ld hl, w00_C800
xor [hl]
ld [hl+], a
ld hl, ROP_SaveMain
jp hl
ROP_RotateBuffer: ; s02_AF85
; s02_ADB8 = ((s02_ADB8 + 1) | $C0) ^ $C0
; s02_ADB8 = (s02_ADB8 + 1) & 0x3F
ld hl, s02_ADB8
ld a, [hl-]
inc a
ld hl, $07FC
or [hl]
ld bc, $C000
xor b
ld hl, s02_ADB8
ld [hl+], a
; Called every 64 times this function is ran
and a
call z, UpdateSaveSpinner
; Save ROPScript pointer
ld hl, sROPScriptBackup
ld a, e
ld [hl+], a
ld [hl], d
; Rotate the entire buffer, using sBufferCarry as carry
ld hl, w00_C800
ld a, [hl+]
ld hl, sBufferCarry
ld [hl+], a
ld bc, $01B0
ld de, w00_C800
ld hl, w00_C800 + 1
ld af, $FF00
call CopyBytes ; bc bytes from hl to de
ld hl, sBufferCarry
ld a, [hl+]
ld hl, w00_C9AF
ld [hl+], a
; Restore ROPScript pointer
ld hl, sROPScriptBackup
ld a, [hl+]
ld d, [hl]
ld e, a
ld hl, ROP_SaveMain
jp hl
ROP_InitCrypto: ; s02_AFE3
; s02_ADB1 = param
ld hl, s02_ADB1
ld a, [de]
ld [hl+], a
inc de
ld a, [de]
ld [hl+], a
inc de
ld a, [de]
ld [hl+], a
inc de
ld a, [de]
ld [hl+], a
inc de
ld hl, ROP_SaveMain
jp hl
ROP_AddChecksum: ; s02_B005
; sSaveBlockChecksum += w00_C800
ld hl, ROPGadget_variable
ld af, $8600 ; add [hl]
ld [hl+], a
ld hl, ROP_s02_B01B
jp hl
ROP_XorChecksum: ; s02_B013
; sSaveBlockChecksum ^= w00_C800
ld hl, ROPGadget_variable
ld af, $AE00 ; xor [hl]
ld [hl+], a
; fallthrough
ROP_s02_B01B: ; s02_B01B
ld hl, sSaveBlockChecksum
ld a, [hl+]
ld hl, w00_C800
add [hl]/xor [hl] ; Modified by caller
ld hl, sSaveBlockChecksum
ld [hl+], a
ld hl, ROP_SaveMain
jp hl
ROP_WriteChecksumByte: ; s02_B033
; w00_C800 = sSaveBlockChecksum
ld hl, sSaveBlockChecksum
ld a, [hl+]
ld hl, w00_C800
ld [hl+], a
ld hl, ROP_SaveMain
jp hl
ROP_StoreByte: ; s02_B045
; *(sSaveSource++) = sSaveBlockChecksum
; *sSaveSource = sSaveBlockChecksum
ld hl, sSaveBlockChecksum
ld b, [hl]
ld hl, sSaveSource
ldw hl, [hl]
ld [hl], b
inc hl
; sSaveSource = sSaveSource + 1
ld b, h
ld c, l
ld hl, sSaveSource
ld [hl], c
inc hl
ld [hl], b
ld hl, ROP_SaveMain
jp hl
ROP_LoadByte: ; s02_B069
; sSaveBlockChecksum = *sSaveSource
ld hl, sSaveSource
ldw hl, [hl]
ld a, [hl+]
ld hl, sSaveBlockChecksum
ld [hl+], a
ld hl, ROP_SaveMain
jp hl
ROP_rept: ; s02_B07D
; do {} while(param[2]--);
; Fetch jump pointer to bc
ld a, [de]
ld c, a
inc de
ld a, [de]
ld b, a
inc de
; If param == 0, return
ld a, [de]
inc de
ld hl, ROP_SaveMain
and a
call z, (jp hl)
; Decrement parameter, if 0, return
dec a
dec de
ld [de], a
inc de
ld hl, ROP_SaveMain
call z, (jp hl)
; Jump to the script at bc
ld d, b
ld a, c
ld e, a
ld hl, ROP_SaveMain
jp hl
ROPScript_SaveMain: ; s02_B0B3
; ==========================================================================
; Start compiling data into save blob, and applying first crypto layer.
; Each "section" of the save blob has its own, fixed crypto seed and checksum.
; ==========================================================================
; sSaveSource = $FCDF
dbw $01, $FCDF ; ROP_SetAddr
; sSaveBlockChecksum = $7F
dbb $02, $7F ; ROP_InitChecksum
; s02_ADB1 = $8C10E62F
dbl $07, $8C10E62F ; ROP_InitCrypto
.s02_B0BD
; rept ($48) {
; w00_C800 = *(sSaveSource++)
db $03 ; ROP_FetchByte
; w00_C800 ^= func(s02_ADB1)
db $05 ; ROP_ApplyCrypto
; sSaveBlockChecksum += w00_C800
db $08 ; ROP_AddChecksum
; rotatebuffer(w00_C800)
db $06 ; ROP_RotateBuffer
; w00_C800 = *(sSaveSource++)
db $03 ; ROP_FetchByte
; sSaveBlockChecksum ^= w00_C800
db $09 ; ROP_XorChecksum
; w00_C800 ^= func(s02_ADB1)
db $05 ; ROP_ApplyCrypto
; rotatebuffer(w00_C800)
db $06 ; ROP_RotateBuffer
; }
dbwb $0C, .s02_B0BD, $48 ; ROP_rept
; w00_C800 = sSaveBlockChecksum
db $0B ; ROP_WriteChecksumByte
; rotatebuffer(w00_C800)
db $06 ; ROP_RotateBuffer
; sSaveSource = $FA7A
dbw $01, $FA7A ; ROP_SetAddr
; sSaveBlockChecksum = $C2
dbb $02, $C2 ; ROP_InitChecksum
; s02_ADB1 = $6AF528C2
dbl $07, $6AF528C2 ; ROP_InitCrypto
.s02_B0D5
; rept ($14) {
; w00_C800 = *(sSaveSource++)
db $03 ; ROP_FetchByte
; w00_C800 ^= func(s02_ADB1)
db $05 ; ROP_ApplyCrypto
; sSaveBlockChecksum ^= w00_C800
db $09 ; ROP_XorChecksum
; rotatebuffer(w00_C800)
db $06 ; ROP_RotateBuffer
; }
dbwb $0C, .s02_B0D5, $14 ; ROP_rept
; w00_C800 = sSaveBlockChecksum
db $0B ; ROP_WriteChecksumByte
; rotatebuffer(w00_C800)
db $06 ; ROP_RotateBuffer
; sSaveSource = $F84E
dbw $01, $F84E ; ROP_SetAddr
.s02_B0E2
; rept ($03) {
; w00_C800 = *(sSaveSource++)
db $03 ; ROP_FetchByte
; rotatebuffer(w00_C800)
db $06 ; ROP_RotateBuffer
; }
dbwb $0C, .s02_B0E2, $03 ; ROP_rept
; sSaveSource = $F859
dbw $01, $F859 ; ROP_SetAddr
; sSaveBlockChecksum = $06
dbb $02, $06 ; ROP_InitChecksum
; s02_ADB1 = $EF7305A6
dbl $07, $EF7305A6 ; ROP_InitCrypto
.s02_B0F2
; rept ($4C) {
; w00_C800 = *(sSaveSource++)
db $03 ; ROP_FetchByte
; w00_C800 ^= func(s02_ADB1)
db $05 ; ROP_ApplyCrypto
; sSaveBlockChecksum ^= w00_C800
db $09 ; ROP_XorChecksum
; rotatebuffer(w00_C800)
db $06 ; ROP_RotateBuffer
; w00_C800 = *(sSaveSource++)
db $03 ; ROP_FetchByte
; w00_C800 ^= func(s02_ADB1)
db $05 ; ROP_ApplyCrypto
; sSaveBlockChecksum += w00_C800
db $08 ; ROP_AddChecksum
; rotatebuffer(w00_C800)
db $06 ; ROP_RotateBuffer
; }
dbwb $0C, .s02_B0F2, $4C ; ROP_rept
; w00_C800 = sSaveBlockChecksum
db $0B ; ROP_WriteChecksumByte
; rotatebuffer(w00_C800)
db $06 ; ROP_RotateBuffer
; sSaveSource = $DE41
dbw $01, $DE41 ; ROP_SetAddr
; s02_ADB1 = $2A7FEC38
dbl $07, $2A7FEC38 ; ROP_InitCrypto
; sSaveBlockChecksum = $3C
dbb $02, $3C ; ROP_InitChecksum
.s02_B10A
; rept ($21) {
; w00_C800 = *(sSaveSource++)
db $03 ; ROP_FetchByte
; sSaveBlockChecksum += w00_C800
db $08 ; ROP_AddChecksum
; w00_C800 ^= func(s02_ADB1)
db $05 ; ROP_ApplyCrypto
; rotatebuffer(w00_C800)
db $06 ; ROP_RotateBuffer
; }
dbwb $0C, .s02_B10A, $21 ; ROP_rept
; w00_C800 = sSaveBlockChecksum
db $0B ; ROP_WriteChecksumByte
; rotatebuffer(w00_C800)
db $06 ; ROP_RotateBuffer
; sSaveSource = $DE99
dbw $01, $DE99 ; ROP_SetAddr
; sSaveBlockChecksum = $E2
dbb $02, $E2 ; ROP_InitChecksum
; s02_ADB1 = $4BFC1115
dbl $07, $4BFC1115 ; ROP_InitCrypto
.s02_B11E
; rept ($40) {
; w00_C800 = *(sSaveSource++)
db $03 ; ROP_FetchByte
; w00_C800 ^= func(s02_ADB1)
db $05 ; ROP_ApplyCrypto
; sSaveBlockChecksum += w00_C800
db $08 ; ROP_AddChecksum
; rotatebuffer(w00_C800)
db $06 ; ROP_RotateBuffer
; }
dbwb $0C, .s02_B11E, $40 ; ROP_rept
; w00_C800 = sSaveBlockChecksum
db $0B ; ROP_WriteChecksumByte
; rotatebuffer(w00_C800)
db $06 ; ROP_RotateBuffer
; s02_ADB1 = $A23F387C
dbl $07, $A23F387C ; ROP_InitCrypto
; sSaveBlockChecksum = $16
dbb $02, $16 ; ROP_InitChecksum
; sSaveSource = $A003
dbw $01, $A003 ; ROP_SetAddr
.s02_B132
; rept ($04) {
; w00_C800 = *(sSaveSource++)
db $03 ; ROP_FetchByte
; w00_C800 ^= func(s02_ADB1)
db $05 ; ROP_ApplyCrypto
; sSaveBlockChecksum ^= w00_C800
db $09 ; ROP_XorChecksum
; sSaveBlockChecksum += w00_C800
db $08 ; ROP_AddChecksum
; rotatebuffer(w00_C800)
db $06 ; ROP_RotateBuffer
; }
dbwb $0C, .s02_B132, $04 ; ROP_rept
; w00_C800 = sSaveBlockChecksum
db $0B ; ROP_WriteChecksumByte
; rotatebuffer(w00_C800)
db $06 ; ROP_RotateBuffer
; ==========================================================================
; End compiling data into save blob.
; ==========================================================================
; ==========================================================================
; Apply second layer of crypto onto the save file as a whole.
; The seed for this crypto is based on random bytes,
; which are stored in the final blob as well.
; ==========================================================================
; sSaveBlockChecksum = $00
dbb $02, $00 ; ROP_InitChecksum
; sSaveSource = $FFE1
dbw $01, $FFE1 ; ROP_SetAddr
; w00_C800 = *(sSaveSource++)
db $03 ; ROP_FetchByte
; sSaveBlockChecksum += w00_C800
db $08 ; ROP_AddChecksum
; sSaveSource = $B16B
dbw $01, $B16B ; ROP_SetAddr
; *(sSaveSource++) = sSaveBlockChecksum
db $0D ; ROP_StoreByte
; rotatebuffer(w00_C800)
db $06 ; ROP_RotateBuffer
; sSaveSource = $FFE2
dbw $01, $FFE2 ; ROP_SetAddr
; w00_C800 = *(sSaveSource++)
db $03 ; ROP_FetchByte
; sSaveBlockChecksum ^= w00_C800
db $09 ; ROP_XorChecksum
; w00_C800 = sSaveBlockChecksum
db $0B ; ROP_WriteChecksumByte
; sSaveSource = $B16C
dbw $01, $B16C ; ROP_SetAddr
; *(sSaveSource++) = sSaveBlockChecksum
db $0D ; ROP_StoreByte
; rotatebuffer(w00_C800)
db $06 ; ROP_RotateBuffer
; sSaveSource = $FF04
dbw $01, $FF04 ; ROP_SetAddr
; w00_C800 = *(sSaveSource++)
db $03 ; ROP_FetchByte
; sSaveBlockChecksum += w00_C800
db $08 ; ROP_AddChecksum
; w00_C800 = sSaveBlockChecksum
db $0B ; ROP_WriteChecksumByte
; sSaveSource = $B16D
dbw $01, $B16D ; ROP_SetAddr
; *(sSaveSource++) = sSaveBlockChecksum
db $0D ; ROP_StoreByte
; rotatebuffer(w00_C800)
db $06 ; ROP_RotateBuffer
; sSaveSource = $FF05
dbw $01, $FF05 ; ROP_SetAddr
; w00_C800 = *(sSaveSource++)
db $03 ; ROP_FetchByte
; sSaveBlockChecksum ^= w00_C800
db $09 ; ROP_XorChecksum
; w00_C800 = sSaveBlockChecksum
db $0B ; ROP_WriteChecksumByte
; sSaveSource = $B16E
dbw $01, $B16E ; ROP_SetAddr
; *(sSaveSource++) = sSaveBlockChecksum
db $0D ; ROP_StoreByte
; rotatebuffer(w00_C800)
db $06 ; ROP_RotateBuffer
; NOTE: The parameter of this command is modified by the commands above.
; s02_ADB1 = $EFBEADDE
dbl $07, $EFBEADDE ; ROP_InitCrypto
; rotatebuffer(w00_C800)
db $06 ; ROP_RotateBuffer
; rotatebuffer(w00_C800)
db $06 ; ROP_RotateBuffer
; sSaveBlockChecksum = $55
dbb $02, $55 ; ROP_InitChecksum
; sSaveSource = $F350
dbw $01, $F350 ; ROP_SetAddr
; *(sSaveSource++) = sSaveBlockChecksum
db $0D ; ROP_StoreByte
; *(sSaveSource++) = sSaveBlockChecksum
db $0D ; ROP_StoreByte
.s02_B178
; rept ($1AB) {
; sSaveSource = $F350
dbw $01, $F350 ; ROP_SetAddr
; sSaveBlockChecksum = *sSaveSource
db $0E ; ROP_LoadByte
; sSaveBlockChecksum += w00_C800
db $08 ; ROP_AddChecksum
; *(sSaveSource++) = sSaveBlockChecksum
db $0D ; ROP_StoreByte
; sSaveBlockChecksum = *sSaveSource
db $0E ; ROP_LoadByte
; sSaveBlockChecksum ^= w00_C800
db $09 ; ROP_XorChecksum
; *(sSaveSource++) = sSaveBlockChecksum
db $0D ; ROP_StoreByte
; w00_C800 ^= func(s02_ADB1)
db $05 ; ROP_ApplyCrypto
; rotatebuffer(w00_C800)
db $06 ; ROP_RotateBuffer
; }
dbwb $0C, .s02_B178, $D5 ; ROP_rept
dbwb $0C, .s02_B178, $D6 ; ROP_rept
; sSaveBlockChecksum = $CC
dbb $02, $CC ; ROP_InitChecksum
; sSaveSource = $ADB1
dbw $01, $ADB1 ; ROP_SetAddr
; sSaveBlockChecksum ^= w00_C800
db $09 ; ROP_XorChecksum
; *(sSaveSource++) = sSaveBlockChecksum
db $0D ; ROP_StoreByte
; rotatebuffer(w00_C800)
db $06 ; ROP_RotateBuffer
; sSaveBlockChecksum ^= w00_C800
db $09 ; ROP_XorChecksum
; *(sSaveSource++) = sSaveBlockChecksum
db $0D ; ROP_StoreByte
; rotatebuffer(w00_C800)
db $06 ; ROP_RotateBuffer
; sSaveBlockChecksum ^= w00_C800
db $09 ; ROP_XorChecksum
; *(sSaveSource++) = sSaveBlockChecksum
db $0D ; ROP_StoreByte
; rotatebuffer(w00_C800)
db $06 ; ROP_RotateBuffer
; sSaveBlockChecksum ^= w00_C800
db $09 ; ROP_XorChecksum
; *(sSaveSource++) = sSaveBlockChecksum
db $0D ; ROP_StoreByte
; rotatebuffer(w00_C800)
db $06 ; ROP_RotateBuffer
; sSaveSource = $F350
dbw $01, $F350 ; ROP_SetAddr
; w00_C800 = *(sSaveSource++)
db $03 ; ROP_FetchByte
; w00_C800 ^= func(s02_ADB1)
db $05 ; ROP_ApplyCrypto
; rotatebuffer(w00_C800)
db $06 ; ROP_RotateBuffer
; w00_C800 = *(sSaveSource++)
db $03 ; ROP_FetchByte
; w00_C800 ^= func(s02_ADB1)
db $05 ; ROP_ApplyCrypto
; rotatebuffer(w00_C800)
db $06 ; ROP_RotateBuffer
; Copy save blob from the buffer to final location in SRAM, and end.
db $04 ; ROP_end
; Temporary save location for the DelayFrame return pointer
sVBlankReturnPointer: dw ; s02_B4E6
VBlankHook_Save: ; s02_B673
; Saving hook code
; Check if the "P" tile is on the screen (part of "PLAYER")
ld a,[$C4CD] ; wTileMap + ???
cp a,"P" ; $8F
ret nz
; Check that we're trying to make a textbox for saving the game
ld hl,[sp+$12]
ldi a,[hl]
cp a,LOW(Text_WouldYouLikeToSaveTheGame)
ret nz
ldd a,[hl]
cp a,HIGH(Text_WouldYouLikeToSaveTheGame)
ret nz
; Change that text pointer to an empty text box (only include the TX_END command)
ld [hl],LOW($0134)
inc hl
ld [hl],HIGH($0134)
; Change the MapTextbox return address to skip to the end of SaveTheGame_yesorno
ld hl,sp+$16
ld [hl],LOW(SaveTheGame_yesorno + 27) ; $4BCA
; Change the SaveTheGame_yesorno return address to skip to SaveMenu.refused
inc hl
inc hl
ld [hl],LOW(SaveMenu.refused)
inc hl
ld [hl],HIGH(SaveMenu.refused)
; Overwrite return pointer for the vblank hook
ld hl,SaveGame
ld a,l
ld [sVBlankReturnPointer],a
ld a,h
ld [sVBlankReturnPointer + 1],a
ld a,BANK(SaveGame)
ld [wVBlankReturnBank],a ; Overwrite return bank?
ret
UninstallVBlankHook: ; s02_B711
ld hl, hTransferVirtualOAM
ld [hl], $3E ; ld a, n8
inc hl
ld [hl], $C4
ret
w00_C800: ds $1b0 ; w00_C800
w00_C9AF: ; w00_C9AF
wOptions: db ; w01_CFCC
wPartyCount: db ; w01_DCD7
w01_DF34: db
wVBlankReturnBank: db ; w01_DF35
wSavingAllowed: db ; w01_DF3A

BIN
pk4/fools.sav

Binary file not shown.

30
pk4/hook.asm

@ -0,0 +1,30 @@
; Break for:
; BC=1A5
; BC=1A6
; BC=1A7
; BC=1A8
; Hook this at 02:AF1F
; Unhook when 02:B13D is read (original: ADBA)
ld a, [$AD42]
ld c, a
ld a, [$AD43]
ld b, a
nop
ld a, c
ret
; Hook this at the end of 02:AFDD
ld a, [$AD42]
ld c, a
ld a, [$AD43]
ld b, a
inc bc
ld a, c
ld [$AD42], a
ld a, b
ld [$AD43], a
ld sp, $AE18
ret

BIN
pk4/orig.sav

Binary file not shown.

41
pk4/ropgadgets.txt

@ -0,0 +1,41 @@
$0394 - and a
$0831 - ld hl, n16
$08F1 - ld bc, n16
$0933 - xor b
$09A2 - ld bc, de, hl, af, n16
$09A4 - ld hl, af, n16
$0D8E - ld c, a
$106A - inc de
$106A - inc de
$1380 - dec de
$1447 - inc hl
$1489 - ld b, a
$1708 - jp hl
$1898 - ld a, [de]
$18D0 - ld a, e
$18DC - ld c, l
$1C85 - ldw hl, [hl]
$1D17 - add hl, bc
$1FA5 - ld e, a
$1FEE - ld [hl], b
$2250 - ld [hl+], a
$2CAD - ld a, c
$2DE0 - or [hl]
$2EC9 - dec a
$3351 - ld [hl], d
$37C2 - inc a
$3E3D - add a
$4128 - ld [de], a
$4404 - ld b, h
$4D6A - ld d, b
$50EC - ld d, [hl]
$65E1 - inc [hl]
$ADBA - ld a, [hl+]
$ADBC - ld a, [hl-]
$ADBE - ld [hl], c
$ADC0 - ld b, [hl]
$ADC2 - add b
$ADC4 - srl a
$ADC7 - xor [hl]
$ADC9 - call z, n16
$ADCC - add [hl]/xor [hl]

196
pk4/ropscript.c

@ -0,0 +1,196 @@
#include <stdio.h>
// Final buffer goes at 3:A100 of the save file
char buf[0x1b0];
int s02_ADB1;
char s02_ADB1_b;
char s02_ADB2;
char s02_ADB3;
char s02_ADB4;
char sSaveBlockChecksum;
char func()
{
s02_ADB1_b = (s02_ADB1 >> 0) & 0xFF;
s02_ADB2 = (s02_ADB1 >> 8) & 0xFF;
s02_ADB3 = (s02_ADB1 >> 16) & 0xFF;
s02_ADB4 = (s02_ADB1 >> 24) & 0xFF;
s02_ADB2 ^= (s02_ADB1_b + 1) ^ s02_ADB4;
s02_ADB3 += s02_ADB2;
s02_ADB4 = s02_ADB2 ^ (s02_ADB4 + (s02_ADB3 / 2));
s02_ADB1 = (s02_ADB4 << 24) | (s02_ADB3 << 16) | (s02_ADB2 << 8) | s02_ADB1_b;
return s02_ADB4;
}
int main()
{
int bufi = 0;
// sSaveSource = $FCDF;
sSaveBlockChecksum = 0x7F;
s02_ADB1 = 0x8C10E62F;
for (int i = 0; i < 0x48; i++) {
buf[bufi] = (char)(bufi & 0xFF);
buf[bufi] ^= func();
sSaveBlockChecksum += buf[bufi];
bufi++;
buf[bufi] = (char)(bufi & 0xFF);
sSaveBlockChecksum ^= buf[bufi];
buf[bufi] ^= func();
bufi++;
}
buf[bufi] = sSaveBlockChecksum;
bufi++;
// sSaveSource = $FA7A;
sSaveBlockChecksum = 0xC2;
s02_ADB1 = 0x6AF528C2;
for (int i = 0; i < 0x14; i++) {
buf[bufi] = (char)(bufi & 0xFF);
buf[bufi] ^= func();
sSaveBlockChecksum ^= buf[bufi];
bufi++;
}
buf[bufi] = sSaveBlockChecksum;
bufi++;
// sSaveSource = $F84E;
for (int i = 0; i < 0x03; i++) {
buf[bufi] = (char)(bufi & 0xFF);
bufi++;
}
// sSaveSource = $F859;
sSaveBlockChecksum = 0x06;
s02_ADB1 = 0xEF7305A6;
for (int i = 0; i < 0x4C; i++) {
buf[bufi] = (char)(bufi & 0xFF);
buf[bufi] ^= func();
sSaveBlockChecksum ^= buf[bufi];
bufi++;
buf[bufi] = (char)(bufi & 0xFF);
buf[bufi] ^= func();
sSaveBlockChecksum += buf[bufi];
bufi++;
}
buf[bufi] = sSaveBlockChecksum;
bufi++;
// sSaveSource = $DE41;
s02_ADB1 = 0x2A7FEC38;
sSaveBlockChecksum = 0x3C;
for (int i = 0; i < 0x21; i++) {
buf[bufi] = (char)(bufi & 0xFF);
sSaveBlockChecksum += buf[bufi];
buf[bufi] ^= func();
bufi++;
}
buf[bufi] = sSaveBlockChecksum;
bufi++;
// sSaveSource = $DE99;
sSaveBlockChecksum = 0xE2;
s02_ADB1 = 0x4BFC1115;
for (int i = 0 ; i < 0x40; i++) {
buf[bufi] = (char)(bufi & 0xFF);
buf[bufi] ^= func();
sSaveBlockChecksum += buf[bufi];
bufi++;
}
buf[bufi] = sSaveBlockChecksum;
bufi++;
s02_ADB1 = 0xA23F387C;
sSaveBlockChecksum = 0x16;
// sSaveSource = $A003;
for (int i = 0; i < 0x04; i++) {
buf[bufi] = (char)(bufi & 0xFF);
buf[bufi] ^= func();
sSaveBlockChecksum ^= buf[bufi];
sSaveBlockChecksum += buf[bufi];
bufi++;
}
buf[bufi] = sSaveBlockChecksum;
bufi++;
sSaveBlockChecksum = 0x00;
// sSaveSource = $FFE1;
buf[bufi] = (char)(bufi & 0xFF);
sSaveBlockChecksum += buf[bufi];
sSaveSource = 0xB16B;
*(sSaveSource++) = sSaveBlockChecksum;
bufi++;
// sSaveSource = $FFE2;
// buf[bufi] = *(sSaveSource++);
// sSaveBlockChecksum ^= buf[bufi];
// buf[bufi] = sSaveBlockChecksum;
// sSaveSource = $B16C;
// *(sSaveSource++) = sSaveBlockChecksum;
// bufi++;
// sSaveSource = $FF04;
// buf[bufi] = *(sSaveSource++);
// sSaveBlockChecksum += buf[bufi];
// buf[bufi] = sSaveBlockChecksum;
// sSaveSource = $B16D;
// *(sSaveSource++) = sSaveBlockChecksum;
// bufi++;
// sSaveSource = $FF05;
// buf[bufi] = *(sSaveSource++);
// sSaveBlockChecksum ^= buf[bufi];
// buf[bufi] = sSaveBlockChecksum;
// sSaveSource = $B16E;
// *(sSaveSource++) = sSaveBlockChecksum;
// bufi++;
// s02_ADB1 = $EFBEADDE;
// bufi++;
// bufi++;
// sSaveBlockChecksum = $55;
// sSaveSource = $F350;
// *(sSaveSource++) = sSaveBlockChecksum;
// *(sSaveSource++) = sSaveBlockChecksum;
// rept ($1AB) {;
// sSaveSource = $F350;
// sSaveBlockChecksum = *sSaveSource;
// sSaveBlockChecksum += buf[bufi];
// *(sSaveSource++) = sSaveBlockChecksum;
// sSaveBlockChecksum = *sSaveSource;
// sSaveBlockChecksum ^= buf[bufi];
// *(sSaveSource++) = sSaveBlockChecksum;
// buf[bufi] ^= func(s02_ADB1);
// bufi++;
// };
// sSaveBlockChecksum = $CC;
// sSaveSource = $ADB1;
// sSaveBlockChecksum ^= buf[bufi];
// *(sSaveSource++) = sSaveBlockChecksum;
// bufi++;
// sSaveBlockChecksum ^= buf[bufi];
// *(sSaveSource++) = sSaveBlockChecksum;
// bufi++;
// sSaveBlockChecksum ^= buf[bufi];
// *(sSaveSource++) = sSaveBlockChecksum;
// bufi++;
// sSaveBlockChecksum ^= buf[bufi];
// *(sSaveSource++) = sSaveBlockChecksum;
// bufi++;
// sSaveSource = $F350;
// buf[bufi] = *(sSaveSource++);
// buf[bufi] ^= func(s02_ADB1);
// bufi++;
// buf[bufi] = *(sSaveSource++);
// buf[bufi] ^= func(s02_ADB1);
// bufi++;
}

72
pokecrystal.diff

@ -0,0 +1,72 @@
# These are the patches I used to speed through the game.
# They include:
# - All pokemon's stats and HP are max when sent into battle
# - HP bars deplete instantly (to avoid destiny bond softlocking the game...)
# - No trainers can spot you (used to solve PK1 mostly)
diff --git a/Makefile b/Makefile
index e213bf63c..812cb9799 100644
--- a/Makefile
+++ b/Makefile
@@ -89,6 +89,7 @@ endif
pokecrystal.gbc: $(crystal_obj) pokecrystal.link
$(RGBLINK) -n pokecrystal.sym -m pokecrystal.map -l pokecrystal.link -o $@ $(crystal_obj)
$(RGBFIX) -Cjv -i BYTE -k 01 -l 0x33 -m 0x10 -p 0 -r 3 -t PM_CRYSTAL $@
+ dd bs=1 if=baserom.gbc of=pokecrystal.gbc count=2 conv=notrunc skip=334 seek=334
tools/sort_symfile.sh pokecrystal.sym
pokecrystal11.gbc: $(crystal11_obj) pokecrystal.link
diff --git a/engine/battle/core.asm b/engine/battle/core.asm
index 38abbb51f..f752e4d46 100644
--- a/engine/battle/core.asm
+++ b/engine/battle/core.asm
@@ -3884,11 +3884,13 @@ InitBattleMon:
ld bc, MON_NAME_LENGTH
call CopyBytes
ld hl, wBattleMonAttack
- ld de, wPlayerStats
+ ld a, $ff
ld bc, PARTYMON_STRUCT_LENGTH - MON_ATK
- call CopyBytes
- call ApplyStatusEffectOnPlayerStats
- call BadgeStatBoosts
+ call ByteFill
+ ld a, $ff
+ ld [wBattleMonHP], a
+ nop
+ nop
ret
BattleCheckPlayerShininess:
diff --git a/engine/overworld/events.asm b/engine/overworld/events.asm
index a84d72db4..9d1dd8d44 100644
--- a/engine/overworld/events.asm
+++ b/engine/overworld/events.asm
@@ -294,8 +294,10 @@ PlayerEvents:
CheckTrainerBattle_GetPlayerEvent:
nop
nop
- call CheckTrainerBattle
- jr nc, .nope
+ nop
+ nop
+ nop
+ jr .nope
ld a, PLAYEREVENT_SEENBYTRAINER
scf
diff --git a/engine/pokemon/health.asm b/engine/pokemon/health.asm
index d11a073b5..0cd97d237 100644
--- a/engine/pokemon/health.asm
+++ b/engine/pokemon/health.asm
@@ -104,7 +104,9 @@ ComputeHPBarPixels:
ret
AnimateHPBar:
- call WaitBGMap
+ ret
+ ret
+ ret
call _AnimateHPBar
call WaitBGMap
ret
Loading…
Cancel
Save