From 7114fdbcf59255a74a24fee0ab7721c406dacb59 Mon Sep 17 00:00:00 2001 From: Connor Olding Date: Sat, 2 Apr 2016 05:58:46 -0700 Subject: [PATCH] first working ROM build of beta quest --- patch/.gitignore | 1 + patch/code.asm | 44 ++++-- patch/common.asm | 32 ++++ patch/extra.asm | 377 ++++++++++++++++++++++++++++++++++++++++++++ patch/mm-randomizer | 26 ++- patch/patch.lua | 25 ++- patch/setup.asm | 152 ++++++++++++++++++ 7 files changed, 637 insertions(+), 20 deletions(-) create mode 100644 patch/common.asm create mode 100644 patch/extra.asm create mode 100644 patch/setup.asm diff --git a/patch/.gitignore b/patch/.gitignore index 0d0b0f4..418961f 100644 --- a/patch/.gitignore +++ b/patch/.gitignore @@ -1,4 +1,5 @@ *.z64 +*.bin patchme lips entrances.asm diff --git a/patch/code.asm b/patch/code.asm index 3f8ca1a..049434a 100644 --- a/patch/code.asm +++ b/patch/code.asm @@ -1,17 +1,41 @@ -[link_save]: 0x801EF670 - [has_completed_intro]: 0x5 - [have_tatl]: 0x22 - [player_name]: 0x2C - [scene_flags]: 0x470 - [week_event_reg]: 0xEF8 - [voidout_type]: 0x3CB0 - [voidout_exit]: 0x3CC4 - [exit_mod_setter]: 0x3F4A - [scene_flags_ingame]: 0x3F68 +.include "common.asm" [starting_exit]: 0x9F87C [default_save]: 0x120DD8 +; 0x8016A2C8 -> 0xC4808 +; 0x8016A2C8 - 0xC4808 = 0x800A5AC0 + +; 0x8016AC0C - 0x8016A2C8 = 0x944 + +.org 0xC4808 + ; if we've already loaded once, don't load again + lbu t0, @start ; 2 + bnez t0, + ; 1 + nop ; 1 + push 4, a0, a1, a2, ra ; 5 + li a0, @start ; 1 + li a1, @vstart ; 2 + li a2, @size ; 2 + jal @DMARomToRam ; 1 + nop ; 1 + pop 4, a0, a1, a2, ra ; 5 ++: + j @dma_hook ; 1 + nop ; 1 +; total overwriten instructions: 23 +; the original function is in setup.asm, +; and is moved into our extra file. +; we have (0x944 / 4 - 23) = 570 words of space here, should we need it. + .word 0xDEADBEEF + +.org 0x9F9A4 ; JR of starting_exit's function + j @load_hook ; tail call + +.org 0x80710 + j @tunic_color_hook + lhu t1, 0x1DB0(t1); original code + .org @starting_exit li t8, 0xD800 ; modified code li t4, 0xD800 ; modified code diff --git a/patch/common.asm b/patch/common.asm new file mode 100644 index 0000000..7e05be9 --- /dev/null +++ b/patch/common.asm @@ -0,0 +1,32 @@ +[vstart]: 0x02EE7040 ; VROM address of our "extra" file +[start]: 0x80780000 ; RAM address of the "extra" file after DMA hook +[size]: 0x5A800 ; this is the most we can store in this region of RAM + +[DMARomToRam]: 0x80080C90 + +[link_save]: 0x801EF670 + [has_completed_intro]: 0x5 + [have_tatl]: 0x22 + [player_name]: 0x2C + [scene_flags]: 0x470 + [week_event_reg]: 0xEF8 + [voidout_type]: 0x3CB0 + [voidout_exit]: 0x3CC4 + [exit_mod_setter]: 0x3F4A + [scene_flags_ingame]: 0x3F68 + +[global_context]: 0x803E6B20 ; FIXME: don't hardcode + +[link_actor]: 0x803FFDB0 ; FIXME: don't hardcode +[link_object_ptr]: 0x803FFFF4 ; actually just an offset of link_actor? +;[link_object_ptr]: 0x244 + +[scene_record_size]: 0x14 + +; TODO: use arithmetic to do this (add that to lips) +; TODO: better yet, add proper label exports to lips +; so you can write the routines anywhere +[dma_hook]: 0x807DA000 +[load_hook]: 0x807DA010 +[setup_hook]: 0x807DA020 +[tunic_color_hook]: 0x807DA030 diff --git a/patch/extra.asm b/patch/extra.asm new file mode 100644 index 0000000..fa54905 --- /dev/null +++ b/patch/extra.asm @@ -0,0 +1,377 @@ + ; note: we check the first byte here to determine if we've been loaded already + .word 0xDEADBEEF ; so this can't be 0x00xxxxxx +whatever: ; debugging stuff + .word 0 + +.include "common.asm" + +hash: +tunic_color: + .word 0xFFFFFFFF + +old_exit: + .half 0 +new_exit: + .half 0 +.align + +rng_seed: + .word 0 + +.include "entrances.asm" +.include "crc32.asm" + +dma_hook: + push 4, 1, ra + jal setup_hook + nop + pop 4, 1, ra + +; original code (includes jr) +.include "setup.asm" + +set_scene_flag: + ; a0: scene number + ; a1: scene word (0-4) + ; a2: bit to set (0-31) + ; v0: new scene flag word + li t0, @link_save + addiu t1, t0, @scene_flags_ingame + li t2, @scene_record_size + multu a0, t2 + mflo t2 + addu t3, t1, t2 + sll t4, a1, 2 ; t4 = a1*sizeof(word) + addu t3, t3, t4 + lw v0, (t3) ; load scene flag word + li t6, 1 + sllv t6, t6, a2 + or v0, v0, t6 ; set flag + jr + sw v0, (t3) ; write it back + +get_event_flag: + ; a0: event flag offset + ; a1: byte offset + ; a2: bit to set (0-7) + ; v0: 1 if set, else 0 + li t0, @link_save + addu t1, t0, a0 + addu t2, t1, a1 + lb v0, (t2) + li t6, 1 + sllv t6, t6, a2 + and v0, v0, t6 + beqz v0, + + cl v0 + li v0, 1 ++: + jr + nop + +set_event_flag: + ; a0: event flag offset + ; a1: byte offset + ; a2: bit to set (0-7) + ; v0: new event flag value + li t0, @link_save + addu t1, t0, a0 + addu t2, t1, a1 + lb v0, (t2) + li t6, 1 + sllv t6, t6, a2 + or v0, v0, t6 + jr + sb v0, (t2) + +tunic_color_hook: + ; copypasta from CloudMax's old hack + ; registers available for use without blowing up: at, t3, t4, a0 + lw t3, @link_object_ptr + lui t4, 0x0001 + sub t3, t3, t4 ; t3 -= 0x10000 + lw t4, tunic_color + sw t4, 0xF184(t3) + sw t4, 0xEFFC(t3) + sw t4, 0xECB4(t3) + sw t4, 0xEB2C(t3) + sw t4, 0xE8F4(t3) + sw t4, 0xE47C(t3) + sw t4, 0xDE74(t3) + sw t4, 0xDDB4(t3) + sw t4, 0xDBDC(t3) + sw t4, 0xD6D4(t3) + sw t4, 0xD1AC(t3) + j 0x801261D8 + lhu v0, 0xF6DC(v0) ; original code + +load_hook: + push 4, s0, s1, ra, 1 + li s0, @link_save + lb t0, @has_completed_intro(s0) + bnez t0, + + li t0, 1 + ; first time setup + sb t0, @has_completed_intro(s0) + sb t0, @have_tatl(s0) + li a0, 0x001A ; deku intro area + li a1, 2 + jal set_scene_flag + li a2, 2 ; "Hey, you! C'mon! Press Z and talk to me!" + li a0, 0x0063 ; inside clock tower + li a1, 1 ; second word + jal set_scene_flag + li a2, 0 ; first bit ("You've met with a terrible fate") + li a0, @week_event_reg + li a1, 31 + jal set_event_flag + li a2, 2 ; Tatl reminding you about the four directions + li a0, @week_event_reg + li a1, 93 + jal set_event_flag + li a2, 3 ; woken turtle once (shortens cutscene) + li a0, @week_event_reg + li a1, 53 + jal set_event_flag + li a2, 6 ; taken turtle once (skips pirates getting wrekt) ++: + addi a0, s0, @player_name + li a2, 0xFFFFFFFF + jal crc32 + li a1, 8 + not v0, v0 + sw v0, hash + sw v0, rng_seed + jal shuffle_all + nop + jpop 4, s0, s1, ra, 1 + +prng: + ; just a reimplementation of the PRNG the game uses. + ; it's from Numerical Recipes in C, by the way. + ; random = random*0x19660D + 0x3C6EF35F; + lw t0, rng_seed + li t1, 0x19660D + multu t0, t1 + li t2, 0x3C6EF35F + mflo t3 + addu v0, t3, t2 + sw v0, rng_seed + jr + nop + +randint: + ; v0 = random integer from 0 to a0; a0 >= 0 + push 4, s0, ra + jal prng + addi s0, a0, 1 + divu v0, s0 + mfhi v0 + jpop 4, s0, ra + +shuffle_all: + push 4, s0, s1, s2, ra + ; https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#The_.22inside-out.22_algorithm + li s0, 0 + li s1, @entries + la s2, shuffles +-: + jal randint + mov a0, s0 + ; s0 is i, v0 is j + sll t0, s0, 2 ; 1<<2 == 2*sizeof(half) + sll t1, v0, 2 ; likewise + addu t0, s2, t0 ; [i] + addu t1, s2, t1 ; [j] + ; a[i] = a[j] + lhu t3, 2(t1) + sh t3, 2(t0) + ; a[j] = source[i] + lhu t4, 0(t0) + sh t4, 2(t1) + ; iterate + addi s0, s0, 1 + bne s0, s1, - + nop ++: + jpop 4, s0, s1, s2, ra + +shuffle_get: + ; a0: exit value + ; v0: shuffled exit value + push 4, ra, 1 + mov v0, a0 + li t0, @entries + li t1, 0 + la t3, shuffles + mov t4, t3 +-: + lhu t5, (t4) + beq a0, t5, + + nop + addi t1, t1, 1 + beq t1, t0, shuffle_get_return + nop + b - + addi t4, t4, 4 ; 2*sizeof(halfword) ++: + lhu v0, 2(t4) +shuffle_get_return: + jpop 4, ra, 1 + +unset_alt_scene: + andi t9, a0, 0x01FF + andi t0, a0, 0xFE00 + ; use poisoned swamp + li at, 0x0C00 + bne t0, at, + + li at, 0x8400 + addu a0, t9, at ++: + ; use frozen mountain + li at, 0x8A00 + bne t0, at, + + li at, 0x9400 + addu s0, t9, at ++: + li at, 0xAE00 + bne t0, at, + + li at, 0x9A00 + addu s0, t9, at ++: + li at, 0xB600 + bne t0, at, + + li at, 0xB400 + addu s0, t9, at ++: + jr + mov v0, a0 + +set_alt_scene: + push 4, s0, ra + mov s0, a0 + ; use clean swamp when odolwa is beaten + li a0, @week_event_reg + li a1, 20 + jal get_event_flag + li a2, 1 + beqz v0, + + nop + andi t9, s0, 0x01FF + andi t0, s0, 0xFE00 + ; can't actually use bnei here because unsignedness, oops + li at, 0x8400 + bne t0, at, + + li at, 0x0C00 + addu s0, t9, at ++: + ; use unfrozen mountain when goht is beaten + li a0, @week_event_reg + li a1, 33 + jal get_event_flag + li a2, 7 + beqz v0, set_alt_scene_return + nop + andi t9, s0, 0x01FF + andi t0, s0, 0xFE00 + li at, 0x9400 + bne t0, at, + + li at, 0x8A00 + addu s0, t9, at ++: + li at, 0x9A00 + bne t0, at, + + li at, 0xAE00 + addu s0, t9, at ++: + li at, 0xB400 + bne t0, at, + + li at, 0xB600 + addu s0, t9, at ++: +set_alt_scene_return: + mov v0, s0 + jpop 4, s0, ra + +shuffle_exit: + push 4, s0, ra + sh a0, old_exit + li t0, @link_save + lw t1, @voidout_type(t0) + ; if this was a death warp, don't use coordinates for respawning + li at, -6 + bne t1, at, + + nop + cl t1 + sw t1, @voidout_type(t0) ++: + ; same for walking between areas in ikana castle + li at, -2 + bne t1, at, + + nop + cl t1 + sw t1, @voidout_type(t0) ++: + ; if this was a void out, don't shuffle + bnez t1, + + mov s0, a0 + ; if this is a cutscene, don't shuffle + lh t2, @exit_mod_setter(t0) + bnei t2, 0xFFEF, + + nop + ; implicitly passes a0 + jal unset_alt_scene + nop + jal shuffle_get + mov a0, v0 + jal set_alt_scene + mov a0, v0 + mov s0, v0 + sh v0, new_exit + ; set woodfall temple as raised after beating odolwa + ; otherwise the swamp won't be cleansed + li at, 0x8601 + bne s0, at, + + li a1, 20 + li a0, @week_event_reg + jal set_event_flag + li a2, 0 ++: + mov v0, s0 + jpop 4, s0, ra + +setup_hook: + push 4, a0, ra + lw a0, @link_save + jal shuffle_exit + andi a0, a0, 0xFFFF + sw v0, @link_save + jpop 4, a0, ra + +.word 0xDEADBEEF + +.org @dma_hook + j dma_hook + nop +.word dma_hook + nop + +.org @setup_hook + j setup_hook + nop +.word setup_hook + nop + +.org @load_hook + j load_hook + nop +.word load_hook + nop + +.org @tunic_color_hook + j tunic_color_hook + nop +.word tunic_color_hook + nop + +.align 0x10 diff --git a/patch/mm-randomizer b/patch/mm-randomizer index 1394a4d..4d9f819 100755 --- a/patch/mm-randomizer +++ b/patch/mm-randomizer @@ -1,26 +1,40 @@ #!/usr/bin/env bash set -e +fast=0 +[[ "$1" == "fast" ]] && fast=1 +[[ "$1" == "test" ]] && fast=2 + inject=../Lua/inject sha1=d6133ace5afaa0882cf214cf88daba39e266c078 extracted=../dump/mm-US10-"$sha1" rom=../../roms/everything/"Legend of Zelda, The - Majora's Mask (U) [!].z64" lips=../Lua/lib/lips + code="0031 V00B3C000" +extra="1552 V02EE7040" if ! [ -d "$extracted" ]; then ../z64dump.py "$rom" mv "$sha1" "$extracted" fi -[ -d patchme ] && rm -r patchme -cp -r "$extracted" patchme +if [ $fast -eq 0 ]; then + [ -d patchme ] && rm -r patchme + cp -r "$extracted" patchme +fi # don't copy entire dir; avoid copying dotfiles (.git) mkdir -p lips cp "$lips"/* lips - cp "$inject/"{crc32,entrances}.asm . -luajit patch.lua code.asm patchme/"$code" -../z64dump.py patchme -mv patchme.z64 mm-randomizer.z64 +#touch patchme/"$extra" +dd if=/dev/zero of=patchme/"$extra" bs=370688 count=1 2>/dev/null + +luajit patch.lua code.asm patchme/"$code" 0 +luajit patch.lua extra.asm patchme/"$extra" 0x80780000 + +if [ $fast -ne 2 ]; then + ../z64dump.py patchme + mv patchme.z64 mm-randomizer.z64 +fi diff --git a/patch/patch.lua b/patch/patch.lua index 6df5ac5..f87c363 100644 --- a/patch/patch.lua +++ b/patch/patch.lua @@ -2,21 +2,38 @@ package.path = package.path..";./?/init.lua" local assemble = require "lips" -local function inject(patch, target) +local function inject(patch, target, offset) + offset = offset or 0 + local f = io.open(target, 'r+b') local function write(pos, line) assert(#line == 2, "that ain't const") -- TODO: write hex dump format of written bytes - --print(("%08X"):format(pos), line) + if pos >= offset then + pos = pos - offset + end + if pos >= 1024*1024*1024 then + print("you probably don't want to do this:") + print(("%08X"):format(pos), line) + return + end f:seek('set', pos) f:write(string.char(tonumber(line, 16))) end -- offset assembly labels so they work properly, and assemble! - assemble(patch, write, {unsafe=true, offset=0}) + assemble(patch, write, {unsafe=true, offset=offset}) f:close() end -inject(arg[1], arg[2]) +local offset = arg[3] +if offset then + if offset:sub(2) == '0x' then + offset = tonumber(offset, 16) + else + offset = tonumber(offset) + end +end +inject(arg[1], arg[2], offset) diff --git a/patch/setup.asm b/patch/setup.asm new file mode 100644 index 0000000..4a878af --- /dev/null +++ b/patch/setup.asm @@ -0,0 +1,152 @@ +; original code +HEX { +27 BD FF 58 AF B1 00 30 3C 11 80 1F AF B0 00 2C +00 80 80 25 26 31 F6 70 AF BF 00 34 8E 22 3C B0 +8E 0E 00 00 24 01 FF FC 10 41 00 04 AF AE 00 A0 +24 01 FF 9D 54 41 00 16 A2 20 3C A7 92 23 10 0E +24 0A 00 01 24 01 FF 9D 30 6F 00 80 11 E0 00 0A +30 78 00 7F A2 38 10 0E A2 00 00 9B 3C 19 80 81 +27 39 58 20 24 09 02 48 AE 09 00 10 AE 19 00 0C +10 00 02 2F 8F BF 00 34 14 41 00 05 A2 2A 3C A7 +24 0B 00 02 10 00 00 02 AE 2B 3C B0 A2 20 3C A7 +8E 23 00 00 24 01 FF FF 54 61 00 0B 96 22 3F 4A +AE 20 00 00 A2 00 00 9B 3C 0C 80 80 25 8C 3F 30 +24 0D 02 10 AE 0D 00 10 AE 0C 00 0C 10 00 02 1C +8F BF 00 34 96 22 3F 4A 34 01 FF EF 10 41 00 03 +34 01 FF F0 54 41 00 52 02 00 20 25 92 2E 0F 19 +00 03 29 03 00 03 22 43 31 CF 00 80 11 E0 00 1D +30 A5 00 1F 24 01 00 4D 54 81 00 04 24 01 00 4A +10 00 00 18 24 04 00 57 24 01 00 4A 54 81 00 04 +24 01 00 5A 10 00 00 13 24 04 00 45 24 01 00 5A +54 81 00 04 24 01 00 59 10 00 00 0E 24 04 00 5B +24 01 00 59 10 81 00 0A 34 18 FF F0 24 01 00 58 +10 81 00 07 24 01 00 19 10 81 00 05 24 01 00 2F +10 81 00 03 24 01 00 68 54 81 00 03 92 39 0F 0C +A6 38 3F 4A 92 39 0F 0C 24 01 00 42 33 29 00 02 +11 20 00 09 3C 19 80 1C 54 81 00 04 24 01 00 43 +10 00 00 05 24 04 00 06 24 01 00 43 14 81 00 02 +34 0A FF F1 A6 2A 3F 4A 92 2B 0F 2C 24 01 00 10 +31 6C 00 20 51 80 00 05 92 2E 0F 2F 14 81 00 02 +34 0D FF F2 A6 2D 3F 4A 92 2E 0F 2F 24 01 00 34 +31 CF 00 80 11 E0 00 07 00 00 00 00 10 81 00 04 +34 18 FF F0 24 01 00 35 14 81 00 02 00 00 00 00 +A6 38 3F 4A 93 39 20 78 24 01 00 2A 02 39 48 21 +91 2A 00 70 11 40 00 06 00 00 00 00 14 81 00 04 +24 01 54 A0 10 61 00 02 34 0B FF F4 A6 2B 3F 4A +0C 04 C1 DA 30 66 00 0F AE 22 00 00 02 00 20 25 +0C 05 CE 20 00 00 28 25 0C 05 8E 01 02 00 20 25 +0C 05 83 28 00 00 00 00 26 04 00 B8 AF A4 00 44 +0C 04 FB BD 8F A5 00 A0 0C 06 8F B0 00 00 20 25 +0C 04 AA 92 00 00 00 00 0C 04 AB 9A 02 00 20 25 +00 00 28 25 02 00 10 25 24 A5 00 01 28 A1 00 04 +24 42 00 04 14 20 FF FC AC 40 07 FC 26 04 02 20 +26 06 08 30 AF A6 00 3C AF A4 00 40 8F A5 00 44 +0C 03 77 74 02 00 38 25 8F A4 00 40 0C 03 78 C2 +24 05 00 07 00 00 40 25 26 04 03 98 8F A5 00 44 +8F A6 00 3C 02 00 38 25 AF A4 00 48 0C 03 77 74 +AF A8 00 4C 8F A4 00 48 0C 03 78 C2 24 05 01 00 +8F A8 00 4C 8F A4 00 48 24 01 04 68 25 08 01 78 +15 01 FF F2 24 84 01 78 8F A4 00 40 24 05 00 7F +AE 04 08 00 A4 80 01 30 0C 03 7F C6 A6 00 08 10 +02 00 20 25 0C 05 1B 9C 26 05 46 B8 0C 06 AA A8 +02 00 20 25 0C 05 61 B5 02 00 20 25 0C 06 A9 84 +02 00 20 25 0C 03 C0 E4 02 00 20 25 0C 03 BF 98 +02 00 20 25 0C 02 BE 24 02 00 20 25 02 00 20 25 +0C 02 C0 14 24 05 00 64 3C 01 00 01 34 21 88 84 +02 01 28 21 0C 03 89 14 02 00 20 25 3C 01 00 01 +34 21 71 04 02 01 20 21 0C 04 D6 32 AF A4 00 44 +02 00 20 25 0C 03 A8 18 26 05 1F 24 96 22 3F 4A +34 01 FF EF 10 41 00 03 34 0C FF EF AE 22 00 08 +A6 2C 3F 4A 8E 2D 00 08 34 01 FF FD 55 A1 00 03 +96 22 3F 4E AE 20 00 08 96 22 3F 4E 34 01 FF FF +50 41 00 04 96 22 00 0C A6 22 00 0C A6 22 3F 52 +96 22 00 0C 34 01 C0 00 00 41 08 2A 10 20 00 02 +28 41 45 55 10 20 00 03 24 0E 00 01 10 00 00 02 +AE 2E 00 10 AE 20 00 10 0C 03 B7 6C 02 00 20 25 +8E 22 3C A8 10 40 00 03 24 01 00 01 54 41 00 07 +A6 20 3D C0 8E 2F 00 08 34 01 FF F0 01 E1 08 2A +54 20 00 0B AE 20 3C AC A6 20 3D C0 0C 04 57 57 +02 00 20 25 8E 38 00 08 AE 20 00 08 33 19 00 0F +27 29 00 01 10 00 00 02 AE 29 3C AC AE 20 3C AC +8E 22 3C AC 8E 23 00 00 A3 A2 00 87 00 43 20 21 +0C 04 C8 CE 30 84 FF FF 8E 26 00 00 8E 25 3C AC +AF A2 00 60 00 A6 20 21 0C 04 C8 DD 30 84 FF FF +02 00 20 25 8F A5 00 60 0C 05 A4 F5 00 40 30 25 +0C 05 8E 7B 02 00 20 25 0C 04 87 F1 02 00 20 25 +96 22 3F 4E 34 01 FF FF 10 41 00 0F 34 01 80 00 +14 41 00 0C 34 18 FF FD 8E 2A 00 18 8E 2C 00 1C +24 0E 00 01 34 0F FF FE 25 4B 00 01 25 8D 00 01 +AE 2B 00 18 AE 2D 00 1C A2 2E 3F 54 10 00 00 02 +A6 2F 3F 4E A6 38 3F 4E 0C 05 95 82 00 00 00 00 +3C 02 80 1F 24 42 3F 60 8C 59 00 00 3C 01 00 01 +34 21 8B 4C A7 20 01 90 8C 49 00 00 02 01 20 21 +A5 20 01 86 0C 05 BF 4B AF A4 00 4C 3C 05 80 20 +3C 06 80 20 84 C6 BB CE 84 A5 BB CC 8F A4 00 4C +00 00 38 25 AF A0 00 10 0C 05 BF 3C AF A0 00 14 +3C 05 80 20 3C 06 80 20 84 C6 BB CE 84 A5 BB CC +8F A4 00 4C 00 00 38 25 0C 05 BF 58 AF A0 00 10 +3C 0A 80 20 8D 4A BB 90 3C 01 00 02 00 30 08 21 +AC 2A 8E 64 3C 01 00 02 3C 0B 80 78 25 6B 00 00 +00 30 08 21 AC 2B 8E 5C 3C 02 80 78 3C 01 00 02 +24 42 46 00 00 30 08 21 AC 22 8E 68 3C 01 00 02 +00 30 08 21 AC 22 8E 58 3C 01 00 02 00 30 08 21 +AC 22 8E 60 3C 01 80 1F AC 20 6D 10 3C 01 00 02 +00 30 08 21 A0 20 8B 4A 3C 01 80 1D A0 20 0D 54 +0C 04 89 98 26 04 08 28 0C 02 41 08 00 00 00 00 +0C 02 1B F4 00 60 20 25 0C 06 00 58 02 00 20 25 +3C 0C 80 17 3C 0D 80 16 25 8C 8F 64 25 AD 61 3C +3C 01 00 02 AE 0C 00 04 AE 0D 00 08 00 30 08 21 +24 0E FF EC A0 2E 88 75 3C 01 00 02 00 30 08 21 +A4 20 88 76 3C 01 00 02 00 30 08 21 A4 20 88 78 +3C 01 00 02 00 30 08 21 A0 20 88 45 3C 01 00 02 +00 30 08 21 A0 20 88 44 8E 2F 3C A8 24 01 00 01 +51 E1 00 16 3C 01 00 02 92 22 3F 55 24 01 00 FF +93 B8 00 87 14 41 00 0B 24 0A 00 FF 8E 22 00 00 +00 58 20 21 0C 04 C8 E8 30 84 FF FF 00 02 C9 C3 +3C 01 00 02 00 30 08 21 33 29 00 7F 10 00 00 0A +A0 29 88 7F 3C 01 00 02 00 30 08 21 A0 22 88 7F +10 00 00 05 A2 2A 3F 55 3C 01 00 02 00 30 08 21 +24 0B 00 02 A0 2B 88 7F 3C 01 00 01 34 21 8E 48 +02 01 20 21 0C 05 92 0B AF A4 00 4C 8F A4 00 4C +0C 05 92 A6 24 05 00 03 3C 05 A0 A0 34 A5 A0 FF +0C 05 92 A3 8F A4 00 4C 0C 05 91 F4 8F A4 00 4C +3C 04 80 1F 0C 05 06 2C 24 84 6D 18 3C 02 80 1F +3C 01 80 1F 3C 0C 80 1F 24 42 6D 4C A0 20 6D 33 +25 84 6D 38 0C 05 03 A0 AC 44 00 00 3C 02 80 1F +24 42 6D 4C 8C 4D 00 00 44 80 20 00 24 0E 00 01 +02 00 20 25 E5 A4 00 08 8C 4F 00 00 A1 EE 00 00 +8C 58 00 00 A3 00 00 0C 8C 59 00 00 A3 20 00 0D +8C 49 00 00 A1 20 00 0E 8C 4A 00 00 A1 40 00 0F +8C 4B 00 00 A1 60 00 10 8C 4C 00 00 A1 80 00 11 +8C 4D 00 00 A1 A0 00 12 8C 4E 00 00 0C 03 C4 B4 +A1 C0 00 13 26 04 00 74 0C 05 CA C1 AF A4 00 4C +0C 05 CA C1 8F A4 00 4C AF A2 00 94 8F A4 00 4C +0C 05 CA B2 00 40 28 25 8F AF 00 94 24 46 00 08 +24 01 FF F0 00 C1 30 24 01 E6 C0 23 03 02 28 21 +0C 04 0B 6F 00 C0 20 25 3C 01 00 01 34 21 80 00 +02 01 10 21 8C 46 08 50 26 05 1C A0 AF A5 00 4C +AF A2 00 48 0C 02 E4 5C 02 00 20 25 3C 01 00 01 +34 21 86 E0 02 01 28 21 AF A5 00 3C 0C 04 BA AA +02 00 20 25 54 40 00 07 8E 39 00 18 02 00 20 25 +0C 04 BA AA 8F A5 00 3C 50 40 FF FD 02 00 20 25 +8E 39 00 18 24 03 00 05 8F AA 00 48 03 23 00 1A +14 60 00 02 00 00 00 00 00 07 00 0D 24 01 FF FF +14 61 00 04 3C 01 80 00 17 21 00 02 00 00 00 00 +00 06 00 0D 00 00 48 10 24 01 00 01 51 20 00 12 +8E 05 1C CC 91 42 06 E3 8F A4 00 4C 02 00 28 25 +10 41 00 03 24 06 01 5A 54 62 00 0B 8E 05 1C CC +44 80 00 00 AF A0 00 18 AF A0 00 1C 44 07 00 00 +AF A0 00 20 AF A0 00 24 E7 A0 00 10 0C 02 EB 18 +E7 A0 00 14 8E 05 1C CC 8F A4 00 40 0C 03 78 3B +AF A5 00 90 8F A5 00 90 3C 01 80 1D AC 20 0D 50 +84 A6 00 1C 24 01 00 FF 8F A4 00 40 30 C6 00 FF +10 C1 00 03 00 00 00 00 0C 03 7E C5 00 C0 28 25 +0C 03 C5 76 8F A4 00 40 0C 04 4A 79 02 00 20 25 +0C 03 ED D6 02 00 20 25 92 0B 08 14 3C 01 80 1F +02 00 20 25 A0 2B 35 86 92 0C 08 15 3C 01 80 1F +8F A5 00 44 0C 04 D7 BA A0 2C 35 87 0C 03 B6 F8 +02 00 20 25 AE 20 3C B0 3C 01 80 1F 3C 04 80 1F +A0 20 6D FC 0C 05 BF 1E 24 84 6D 50 8F BF 00 34 +8F B0 00 2C 8F B1 00 30 03 E0 00 08 27 BD 00 A8 +00 00 00 00 +}