From 4e6a401484b8e709bd5c33b3e0d4ba02ccf13155 Mon Sep 17 00:00:00 2001 From: Ethan Roseman Date: Sat, 19 Sep 2020 11:16:02 -0400 Subject: [PATCH] byte/flag stuff --- .../code_dba20_len_350/get_area_byte.s | 16 - .../code_dba20_len_350/get_area_flag.s | 26 -- .../code_dba20_len_350/get_global_byte.s | 11 - .../code_dba20_len_350/set_area_byte.s | 12 - .../code_dba20_len_350/set_global_byte.s | 12 - .../code_e92d0_len_5da0/EnableModel.s | 41 --- .../code_e92d0_len_5da0/GetModelCenter.s | 38 -- diff.py | 328 +++++++++++++----- include/common_structs.h | 29 ++ include/functions.h | 6 + include/variables.h | 5 + src/code_a5dd0_len_114e0.c | 2 +- src/code_dba20_len_350.c | 97 +++++- src/code_e92d0_len_5da0.c | 82 ++++- undefined_syms.txt | 5 + 15 files changed, 450 insertions(+), 260 deletions(-) delete mode 100644 asm/nonmatchings/code_dba20_len_350/get_area_byte.s delete mode 100644 asm/nonmatchings/code_dba20_len_350/get_area_flag.s delete mode 100644 asm/nonmatchings/code_dba20_len_350/get_global_byte.s delete mode 100644 asm/nonmatchings/code_dba20_len_350/set_area_byte.s delete mode 100644 asm/nonmatchings/code_dba20_len_350/set_global_byte.s delete mode 100644 asm/nonmatchings/code_e92d0_len_5da0/EnableModel.s delete mode 100644 asm/nonmatchings/code_e92d0_len_5da0/GetModelCenter.s diff --git a/asm/nonmatchings/code_dba20_len_350/get_area_byte.s b/asm/nonmatchings/code_dba20_len_350/get_area_byte.s deleted file mode 100644 index e0546bd6eb..0000000000 --- a/asm/nonmatchings/code_dba20_len_350/get_area_byte.s +++ /dev/null @@ -1,16 +0,0 @@ -.set noat # allow manual use of $at -.set noreorder # don't insert nops after branches - - -glabel get_area_byte -/* 0DBD50 80145650 3C02800E */ lui $v0, 0x800e -/* 0DBD54 80145654 00441021 */ addu $v0, $v0, $a0 -/* 0DBD58 80145658 8042BF90 */ lb $v0, -0x4070($v0) -/* 0DBD5C 8014565C 03E00008 */ jr $ra -/* 0DBD60 80145660 00000000 */ nop - -/* 0DBD64 80145664 00000000 */ nop -/* 0DBD68 80145668 00000000 */ nop -/* 0DBD6C 8014566C 00000000 */ nop - - diff --git a/asm/nonmatchings/code_dba20_len_350/get_area_flag.s b/asm/nonmatchings/code_dba20_len_350/get_area_flag.s deleted file mode 100644 index cb334c5c93..0000000000 --- a/asm/nonmatchings/code_dba20_len_350/get_area_flag.s +++ /dev/null @@ -1,26 +0,0 @@ -.set noat # allow manual use of $at -.set noreorder # don't insert nops after branches - - -glabel get_area_flag -/* 0DBCF0 801455F0 0080102D */ daddu $v0, $a0, $zero -/* 0DBCF4 801455F4 04410002 */ bgez $v0, .L80145600 -/* 0DBCF8 801455F8 0040182D */ daddu $v1, $v0, $zero -/* 0DBCFC 801455FC 2443001F */ addiu $v1, $v0, 0x1f -.L80145600: -/* 0DBD00 80145600 00031943 */ sra $v1, $v1, 5 -/* 0DBD04 80145604 00032140 */ sll $a0, $v1, 5 -/* 0DBD08 80145608 00442023 */ subu $a0, $v0, $a0 -/* 0DBD0C 8014560C 00031880 */ sll $v1, $v1, 2 -/* 0DBD10 80145610 24020001 */ addiu $v0, $zero, 1 -/* 0DBD14 80145614 3C01800E */ lui $at, 0x800e -/* 0DBD18 80145618 00230821 */ addu $at, $at, $v1 -/* 0DBD1C 8014561C 8C23BF70 */ lw $v1, -0x4090($at) -/* 0DBD20 80145620 00821004 */ sllv $v0, $v0, $a0 -/* 0DBD24 80145624 00621024 */ and $v0, $v1, $v0 -/* 0DBD28 80145628 54400001 */ bnel $v0, $zero, .L80145630 -/* 0DBD2C 8014562C 24020001 */ addiu $v0, $zero, 1 -.L80145630: -/* 0DBD30 80145630 03E00008 */ jr $ra -/* 0DBD34 80145634 00000000 */ nop - diff --git a/asm/nonmatchings/code_dba20_len_350/get_global_byte.s b/asm/nonmatchings/code_dba20_len_350/get_global_byte.s deleted file mode 100644 index 83900ec067..0000000000 --- a/asm/nonmatchings/code_dba20_len_350/get_global_byte.s +++ /dev/null @@ -1,11 +0,0 @@ -.set noat # allow manual use of $at -.set noreorder # don't insert nops after branches - - -glabel get_global_byte -/* 0DBC38 80145538 3C02800E */ lui $v0, 0x800e -/* 0DBC3C 8014553C 00441021 */ addu $v0, $v0, $a0 -/* 0DBC40 80145540 8042BD70 */ lb $v0, -0x4290($v0) -/* 0DBC44 80145544 03E00008 */ jr $ra -/* 0DBC48 80145548 00000000 */ nop - diff --git a/asm/nonmatchings/code_dba20_len_350/set_area_byte.s b/asm/nonmatchings/code_dba20_len_350/set_area_byte.s deleted file mode 100644 index 0d53dc2238..0000000000 --- a/asm/nonmatchings/code_dba20_len_350/set_area_byte.s +++ /dev/null @@ -1,12 +0,0 @@ -.set noat # allow manual use of $at -.set noreorder # don't insert nops after branches - - -glabel set_area_byte -/* 0DBD38 80145638 3C02800E */ lui $v0, 0x800e -/* 0DBD3C 8014563C 2442ACC0 */ addiu $v0, $v0, -0x5340 -/* 0DBD40 80145640 00822021 */ addu $a0, $a0, $v0 -/* 0DBD44 80145644 808212D0 */ lb $v0, 0x12d0($a0) -/* 0DBD48 80145648 03E00008 */ jr $ra -/* 0DBD4C 8014564C A08512D0 */ sb $a1, 0x12d0($a0) - diff --git a/asm/nonmatchings/code_dba20_len_350/set_global_byte.s b/asm/nonmatchings/code_dba20_len_350/set_global_byte.s deleted file mode 100644 index 5c8a96b50c..0000000000 --- a/asm/nonmatchings/code_dba20_len_350/set_global_byte.s +++ /dev/null @@ -1,12 +0,0 @@ -.set noat # allow manual use of $at -.set noreorder # don't insert nops after branches - - -glabel set_global_byte -/* 0DBC20 80145520 3C02800E */ lui $v0, 0x800e -/* 0DBC24 80145524 2442ACC0 */ addiu $v0, $v0, -0x5340 -/* 0DBC28 80145528 00822021 */ addu $a0, $a0, $v0 -/* 0DBC2C 8014552C 808210B0 */ lb $v0, 0x10b0($a0) -/* 0DBC30 80145530 03E00008 */ jr $ra -/* 0DBC34 80145534 A08510B0 */ sb $a1, 0x10b0($a0) - diff --git a/asm/nonmatchings/code_e92d0_len_5da0/EnableModel.s b/asm/nonmatchings/code_e92d0_len_5da0/EnableModel.s deleted file mode 100644 index 359a225eed..0000000000 --- a/asm/nonmatchings/code_e92d0_len_5da0/EnableModel.s +++ /dev/null @@ -1,41 +0,0 @@ -.set noat # allow manual use of $at -.set noreorder # don't insert nops after branches - - -glabel EnableModel -/* 0EDC38 802C9288 27BDFFE0 */ addiu $sp, $sp, -0x20 -/* 0EDC3C 802C928C AFB10014 */ sw $s1, 0x14($sp) -/* 0EDC40 802C9290 0080882D */ daddu $s1, $a0, $zero -/* 0EDC44 802C9294 AFBF0018 */ sw $ra, 0x18($sp) -/* 0EDC48 802C9298 AFB00010 */ sw $s0, 0x10($sp) -/* 0EDC4C 802C929C 8E30000C */ lw $s0, 0xc($s1) -/* 0EDC50 802C92A0 8E050000 */ lw $a1, ($s0) -/* 0EDC54 802C92A4 0C0B1EAF */ jal get_variable -/* 0EDC58 802C92A8 26100004 */ addiu $s0, $s0, 4 -/* 0EDC5C 802C92AC 0C046C04 */ jal get_model_list_index_from_tree_index -/* 0EDC60 802C92B0 0040202D */ daddu $a0, $v0, $zero -/* 0EDC64 802C92B4 0220202D */ daddu $a0, $s1, $zero -/* 0EDC68 802C92B8 8E050000 */ lw $a1, ($s0) -/* 0EDC6C 802C92BC 0C0B1EAF */ jal get_variable -/* 0EDC70 802C92C0 0040802D */ daddu $s0, $v0, $zero -/* 0EDC74 802C92C4 0200202D */ daddu $a0, $s0, $zero -/* 0EDC78 802C92C8 0C046B4C */ jal get_model_from_list_index -/* 0EDC7C 802C92CC 0040802D */ daddu $s0, $v0, $zero -/* 0EDC80 802C92D0 12000004 */ beqz $s0, .L802C92E4 -/* 0EDC84 802C92D4 0040182D */ daddu $v1, $v0, $zero -/* 0EDC88 802C92D8 94620000 */ lhu $v0, ($v1) -/* 0EDC8C 802C92DC 080B24BB */ j .L802C92EC -/* 0EDC90 802C92E0 3042FFFD */ andi $v0, $v0, 0xfffd - -.L802C92E4: -/* 0EDC94 802C92E4 94620000 */ lhu $v0, ($v1) -/* 0EDC98 802C92E8 34420002 */ ori $v0, $v0, 2 -.L802C92EC: -/* 0EDC9C 802C92EC A4620000 */ sh $v0, ($v1) -/* 0EDCA0 802C92F0 8FBF0018 */ lw $ra, 0x18($sp) -/* 0EDCA4 802C92F4 8FB10014 */ lw $s1, 0x14($sp) -/* 0EDCA8 802C92F8 8FB00010 */ lw $s0, 0x10($sp) -/* 0EDCAC 802C92FC 24020002 */ addiu $v0, $zero, 2 -/* 0EDCB0 802C9300 03E00008 */ jr $ra -/* 0EDCB4 802C9304 27BD0020 */ addiu $sp, $sp, 0x20 - diff --git a/asm/nonmatchings/code_e92d0_len_5da0/GetModelCenter.s b/asm/nonmatchings/code_e92d0_len_5da0/GetModelCenter.s deleted file mode 100644 index 942b0f1188..0000000000 --- a/asm/nonmatchings/code_e92d0_len_5da0/GetModelCenter.s +++ /dev/null @@ -1,38 +0,0 @@ -.set noat # allow manual use of $at -.set noreorder # don't insert nops after branches - - -glabel GetModelCenter -/* 0ED930 802C8F80 27BDFFC0 */ addiu $sp, $sp, -0x40 -/* 0ED934 802C8F84 AFB00038 */ sw $s0, 0x38($sp) -/* 0ED938 802C8F88 0080802D */ daddu $s0, $a0, $zero -/* 0ED93C 802C8F8C AFBF003C */ sw $ra, 0x3c($sp) -/* 0ED940 802C8F90 8E02000C */ lw $v0, 0xc($s0) -/* 0ED944 802C8F94 0C0B1EAF */ jal get_variable -/* 0ED948 802C8F98 8C450000 */ lw $a1, ($v0) -/* 0ED94C 802C8F9C 3044FFFF */ andi $a0, $v0, 0xffff -/* 0ED950 802C8FA0 27A50020 */ addiu $a1, $sp, 0x20 -/* 0ED954 802C8FA4 27A2002C */ addiu $v0, $sp, 0x2c -/* 0ED958 802C8FA8 AFA20010 */ sw $v0, 0x10($sp) -/* 0ED95C 802C8FAC 27A20030 */ addiu $v0, $sp, 0x30 -/* 0ED960 802C8FB0 AFA20014 */ sw $v0, 0x14($sp) -/* 0ED964 802C8FB4 27A20034 */ addiu $v0, $sp, 0x34 -/* 0ED968 802C8FB8 27A60024 */ addiu $a2, $sp, 0x24 -/* 0ED96C 802C8FBC 27A70028 */ addiu $a3, $sp, 0x28 -/* 0ED970 802C8FC0 0C046C3B */ jal get_model_center_and_size -/* 0ED974 802C8FC4 AFA20018 */ sw $v0, 0x18($sp) -/* 0ED978 802C8FC8 C7A00020 */ lwc1 $f0, 0x20($sp) -/* 0ED97C 802C8FCC 4600010D */ trunc.w.s $f4, $f0 -/* 0ED980 802C8FD0 E6040084 */ swc1 $f4, 0x84($s0) -/* 0ED984 802C8FD4 C7A00024 */ lwc1 $f0, 0x24($sp) -/* 0ED988 802C8FD8 C7A20028 */ lwc1 $f2, 0x28($sp) -/* 0ED98C 802C8FDC 4600010D */ trunc.w.s $f4, $f0 -/* 0ED990 802C8FE0 E6040088 */ swc1 $f4, 0x88($s0) -/* 0ED994 802C8FE4 4600110D */ trunc.w.s $f4, $f2 -/* 0ED998 802C8FE8 E604008C */ swc1 $f4, 0x8c($s0) -/* 0ED99C 802C8FEC 8FBF003C */ lw $ra, 0x3c($sp) -/* 0ED9A0 802C8FF0 8FB00038 */ lw $s0, 0x38($sp) -/* 0ED9A4 802C8FF4 24020002 */ addiu $v0, $zero, 2 -/* 0ED9A8 802C8FF8 03E00008 */ jr $ra -/* 0ED9AC 802C8FFC 27BD0040 */ addiu $sp, $sp, 0x40 - diff --git a/diff.py b/diff.py index 013d935948..0e1da09deb 100755 --- a/diff.py +++ b/diff.py @@ -1,48 +1,71 @@ #!/usr/bin/env python3 import sys -import re -import os -import ast -import argparse -import subprocess -import collections -import difflib -import string -import itertools -import threading -import queue -import time - def fail(msg): print(msg, file=sys.stderr) sys.exit(1) - -MISSING_PREREQUISITES = ( - "Missing prerequisite python module {}. " - "Run `python3 -m pip install --user colorama ansiwrap attrs watchdog python-Levenshtein cxxfilt` to install prerequisites (cxxfilt only needed with --source)." -) - -try: - import attr - from colorama import Fore, Style, Back - import ansiwrap - import watchdog -except ModuleNotFoundError as e: - fail(MISSING_PREREQUISITES.format(e.name)) - # Prefer to use diff_settings.py from the current working directory sys.path.insert(0, ".") try: import diff_settings except ModuleNotFoundError: fail("Unable to find diff_settings.py in the same directory.") +sys.path.pop(0) -# ==== CONFIG ==== +# ==== COMMAND-LINE ==== + +try: + import argcomplete # type: ignore +except ModuleNotFoundError: + argcomplete = None +import argparse parser = argparse.ArgumentParser(description="Diff MIPS assembly.") -parser.add_argument("start", help="Function name or address to start diffing from.") + +start_argument = parser.add_argument("start", help="Function name or address to start diffing from.") +if argcomplete: + def complete_symbol(**kwargs): + prefix = kwargs["prefix"] + if prefix == "": + # skip reading the map file, which would + # result in a lot of useless completions + return [] + parsed_args = kwargs["parsed_args"] + config = {} + diff_settings.apply(config, parsed_args) + mapfile = config.get("mapfile") + if not mapfile: + return [] + completes = [] + with open(mapfile) as f: + data = f.read() + # assume symbols are prefixed by a space character + search = f" {prefix}" + pos = data.find(search) + while pos != -1: + # skip the space character in the search string + pos += 1 + # assume symbols are suffixed by either a space + # character or a (unix-style) line return + spacePos = data.find(" ", pos) + lineReturnPos = data.find("\n", pos) + if lineReturnPos == -1: + endPos = spacePos + elif spacePos == -1: + endPos = lineReturnPos + else: + endPos = min(spacePos, lineReturnPos) + if endPos == -1: + match = data[pos:] + pos = -1 + else: + match = data[pos:endPos] + pos = data.find(search, endPos) + completes.append(match) + return completes + start_argument.completer = complete_symbol + parser.add_argument("end", nargs="?", help="Address to end diff at.") parser.add_argument( "-o", @@ -52,6 +75,7 @@ parser.add_argument( ) parser.add_argument( "-e", + "--elf", dest="diff_elf_symbol", help="Diff a given function in two ELFs, one being stripped and the other one non-stripped. Requires objdump from binutils 2.33+.", ) @@ -132,6 +156,14 @@ parser.add_argument( help="Automatically update when source/object files change. " "Recommended in combination with -m.", ) +parser.add_argument( + "-3", + "--threeway", + dest="threeway", + action="store_true", + help="Show a three-way diff between target asm, current asm, and asm " + "prior to -w rebuild. Requires -w.", +) parser.add_argument( "--width", dest="column_width", @@ -146,7 +178,6 @@ parser.add_argument( choices=["levenshtein", "difflib"], help="Diff algorithm to use.", ) - parser.add_argument( "--max-size", "--max-lines", @@ -158,12 +189,44 @@ parser.add_argument( # Project-specific flags, e.g. different versions/make arguments. if hasattr(diff_settings, "add_custom_arguments"): - diff_settings.add_custom_arguments(parser) + diff_settings.add_custom_arguments(parser) # type: ignore + +if argcomplete: + argcomplete.autocomplete(parser) + +# ==== IMPORTS ==== + +import re +import os +import ast +import subprocess +import difflib +import string +import itertools +import threading +import queue +import time +from typing import Any, Dict, List, NamedTuple, Optional, Set, Tuple, Union + + +MISSING_PREREQUISITES = ( + "Missing prerequisite python module {}. " + "Run `python3 -m pip install --user colorama ansiwrap watchdog python-Levenshtein cxxfilt` to install prerequisites (cxxfilt only needed with --source)." +) + +try: + from colorama import Fore, Style, Back # type: ignore + import ansiwrap # type: ignore + import watchdog # type: ignore +except ModuleNotFoundError as e: + fail(MISSING_PREREQUISITES.format(e.name)) + +# ==== CONFIG ==== args = parser.parse_args() # Set imgs, map file and make flags in a project-specific manner. -config = {} +config: Dict[str, Any] = {} diff_settings.apply(config, args) arch = config.get("arch", "mips") @@ -199,13 +262,13 @@ FS_WATCH_EXTENSIONS = [".c", ".h"] if args.algorithm == "levenshtein": try: - import Levenshtein + import Levenshtein # type: ignore except ModuleNotFoundError as e: fail(MISSING_PREREQUISITES.format(e.name)) if args.source: try: - import cxxfilt + import cxxfilt # type: ignore except ModuleNotFoundError as e: fail(MISSING_PREREQUISITES.format(e.name)) @@ -538,22 +601,21 @@ def process_mips_reloc(row, prev): return before + repl + after -def cleanup_whitespace(line): - return "".join(f"{o:<8s}" for o in line.strip().split("\t")) +def pad_mnemonic(line): + if "\t" not in line: + return line + mn, args = line.split("\t", 1) + return f"{mn:<7s} {args}" -Line = collections.namedtuple( - "Line", - [ - "mnemonic", - "diff_row", - "original", - "line_num", - "branch_target", - "source_lines", - "comment", - ], -) +class Line(NamedTuple): + mnemonic: str + diff_row: str + original: str + line_num: str + branch_target: Optional[str] + source_lines: List[str] + comment: Optional[str] def process(lines): @@ -565,6 +627,7 @@ def process(lines): lines.pop() output = [] + stop_after_delay_slot = False for row in lines: if args.diff_obj and (">:" in row or not row): continue @@ -634,18 +697,15 @@ def process(lines): source_lines = [] if args.stop_jrra and mnemonic == "jr" and row_parts[1].strip() == "ra": + stop_after_delay_slot = True + elif stop_after_delay_slot: break - # Cleanup whitespace, after relocation fixups have happened - output = [ - line._replace(original=cleanup_whitespace(line.original)) for line in output - ] - return output def format_single_line_diff(line1, line2, column_width): - return f"{ansi_ljust(line1,column_width)}{ansi_ljust(line2,column_width)}" + return f"{ansi_ljust(line1,column_width)}{line2}" class SymbolColorer: @@ -743,11 +803,27 @@ def diff_sequences(seq1, seq2): return Levenshtein.opcodes(seq1, seq2) -def do_diff(basedump, mydump): - output = [] +class OutputLine: + base: Optional[str] + fmt2: str + key2: str - # TODO: status line? - # output.append(sha1sum(mydump)) + def __init__(self, base: Optional[str], fmt2: str, key2: str) -> None: + self.base = base + self.fmt2 = fmt2 + self.key2 = key2 + + def __eq__(self, other: object) -> bool: + if not isinstance(other, OutputLine): + return NotImplemented + return self.key2 == other.key2 + + def __hash__(self) -> int: + return hash(self.key2) + + +def do_diff(basedump: str, mydump: str) -> List[OutputLine]: + output: List[OutputLine] = [] lines1 = process(basedump.split("\n")) lines2 = process(mydump.split("\n")) @@ -758,8 +834,8 @@ def do_diff(basedump, mydump): sc4 = SymbolColorer(4) sc5 = SymbolColorer(0) sc6 = SymbolColorer(0) - bts1 = set() - bts2 = set() + bts1: Set[str] = set() + bts2: Set[str] = set() if args.show_branches: for (lines, btset, sc) in [ @@ -856,40 +932,36 @@ def do_diff(basedump, mydump): out2 = line2.original elif line1: line_prefix = "<" - line_color1 = line_color2 = sym_color = Fore.RED + line_color1 = sym_color = Fore.RED out1 = line1.original out2 = "" elif line2: line_prefix = ">" - line_color1 = line_color2 = sym_color = Fore.GREEN + line_color2 = sym_color = Fore.GREEN out1 = "" out2 = line2.original - in_arrow1 = " " - in_arrow2 = " " - out_arrow1 = "" - out_arrow2 = "" - - if args.show_branches and line1: - if line1.line_num in bts1: - in_arrow1 = sc5.color_symbol(line1.line_num, "~>") + line_color1 - if line1.branch_target is not None: - out_arrow1 = " " + sc5.color_symbol(line1.branch_target + ":", "~>") - if args.show_branches and line2: - if line2.line_num in bts2: - in_arrow2 = sc6.color_symbol(line2.line_num, "~>") + line_color2 - if line2.branch_target is not None: - out_arrow2 = " " + sc6.color_symbol(line2.branch_target + ":", "~>") - if args.source and line2 and line2.comment: out2 += f" {line2.comment}" - line_num1 = line1.line_num if line1 else "" - line_num2 = line2.line_num if line2 else "" + def format_part(out: str, line: Optional[Line], line_color: str, btset: Set[str], sc: SymbolColorer) -> Optional[str]: + if line is None: + return None + in_arrow = " " + out_arrow = "" + if args.show_branches: + if line.line_num in btset: + in_arrow = sc.color_symbol(line.line_num, "~>") + line_color + if line.branch_target is not None: + out_arrow = " " + sc.color_symbol(line.branch_target + ":", "~>") + out = pad_mnemonic(out) + return f"{line_color}{line.line_num} {in_arrow} {out}{Style.RESET_ALL}{out_arrow}" - out1 = f"{line_color1}{line_num1} {in_arrow1} {out1}{Style.RESET_ALL}{out_arrow1}" - out2 = f"{line_color2}{line_num2} {in_arrow2} {out2}{Style.RESET_ALL}{out_arrow2}" - mid = f"{sym_color}{line_prefix} " + part1 = format_part(out1, line1, line_color1, bts1, sc5) + part2 = format_part(out2, line2, line_color2, bts2, sc6) + key2 = line2.original if line2 else "" + + mid = f"{sym_color}{line_prefix}" if line2: for source_line in line2.source_lines: @@ -907,22 +979,82 @@ def do_diff(basedump, mydump): ) except: pass - output.append( - format_single_line_diff( - "", - f" {color}{source_line}{Style.RESET_ALL}", - args.column_width, - ) - ) + output.append(OutputLine(None, f" {color}{source_line}{Style.RESET_ALL}", source_line)) - output.append(format_single_line_diff(out1, mid + out2, args.column_width)) + fmt2 = mid + " " + (part2 or "") + output.append(OutputLine(part1, fmt2, key2)) - return output[args.skip_lines :] + return output + + +def chunk_diff(diff: List[OutputLine]) -> List[Union[List[OutputLine], OutputLine]]: + cur_right: List[OutputLine] = [] + chunks: List[Union[List[OutputLine], OutputLine]] = [] + for output_line in diff: + if output_line.base is not None: + chunks.append(cur_right) + chunks.append(output_line) + cur_right = [] + else: + cur_right.append(output_line) + chunks.append(cur_right) + return chunks + + +def format_diff(old_diff: List[OutputLine], new_diff: List[OutputLine]) -> Tuple[str, List[str]]: + old_chunks = chunk_diff(old_diff) + new_chunks = chunk_diff(new_diff) + output: List[Tuple[str, OutputLine, OutputLine]] = [] + assert len(old_chunks) == len(new_chunks), "same target" + empty = OutputLine("", "", "") + for old_chunk, new_chunk in zip(old_chunks, new_chunks): + if isinstance(old_chunk, list): + assert isinstance(new_chunk, list) + if not old_chunk and not new_chunk: + # Most of the time lines sync up without insertions/deletions, + # and there's no interdiffing to be done. + continue + differ = difflib.SequenceMatcher(a=old_chunk, b=new_chunk, autojunk=False) + for (tag, i1, i2, j1, j2) in differ.get_opcodes(): + if tag in ["equal", "replace"]: + for i, j in zip(range(i1, i2), range(j1, j2)): + output.append(("", old_chunk[i], new_chunk[j])) + elif tag == "insert": + for j in range(j1, j2): + output.append(("", empty, new_chunk[j])) + else: + for i in range(i1, i2): + output.append(("", old_chunk[i], empty)) + else: + assert isinstance(new_chunk, OutputLine) + # old_chunk.base and new_chunk.base have the same text since + # both diffs are based on the same target, but they might + # differ in color. Use the new version. + output.append((new_chunk.base or "", old_chunk, new_chunk)) + + # TODO: status line, with e.g. approximate permuter score? + width = args.column_width + if args.threeway: + header_line = "TARGET".ljust(width) + " CURRENT".ljust(width) + " PREVIOUS" + diff_lines = [ + ansi_ljust(base, width) + + ansi_ljust(new.fmt2, width) + + (old.fmt2 or "-" if old != new else "") + for (base, old, new) in output + ] + else: + header_line = "" + diff_lines = [ + ansi_ljust(base, width) + new.fmt2 + for (base, old, new) in output + if base or new.key2 + ] + return header_line, diff_lines def debounced_fs_watch(targets, outq, debounce_delay): - import watchdog.events - import watchdog.observers + import watchdog.events # type: ignore + import watchdog.observers # type: ignore class WatchEventHandler(watchdog.events.FileSystemEventHandler): def __init__(self, queue, file_targets): @@ -993,12 +1125,18 @@ class Display: self.basedump = basedump self.mydump = mydump self.emsg = None + self.last_diff_output = None def run_less(self): if self.emsg is not None: output = self.emsg else: - output = "\n".join(do_diff(self.basedump, self.mydump)) + diff_output = do_diff(self.basedump, self.mydump) + last_diff_output = self.last_diff_output or diff_output + self.last_diff_output = diff_output + header, diff_lines = format_diff(last_diff_output, diff_output) + header_lines = [header] if header else [] + output = "\n".join(header_lines + diff_lines[args.skip_lines :]) # Pipe the output through 'tail' and only then to less, to ensure the # write call doesn't block. ('tail' has to buffer all its input before diff --git a/include/common_structs.h b/include/common_structs.h index d21831fa92..0efa114222 100644 --- a/include/common_structs.h +++ b/include/common_structs.h @@ -1429,4 +1429,33 @@ typedef struct EncounterStatus { /* 0x98 */ s32 unk_98; } EncounterStatus; // size = 0x9C +typedef struct SaveData { + /* 0x0000 */ char magicString[16]; /* "Mario Story 006" string */ + /* 0x0010 */ s8 pad[32]; /* always zero */ + /* 0x0030 */ s32 crc1; + /* 0x0034 */ s32 crc2; + /* 0x0038 */ s32 saveSlot; + /* 0x003C */ s32 saveCount; + /* 0x0040 */ struct PlayerData player; + /* 0x0380 */ char unk_380[0xE0]; + /* 0x0460 */ s32 starPoints; + /* 0x0464 */ char unk_464[4]; + /* 0x0468 */ s16 areaID; + /* 0x046A */ s16 mapID; + /* 0x046C */ s16 entryID; + /* 0x046E */ char unk_46E[2]; + /* 0x0470 */ s32 enemyDefeatFlags[720]; + /* 0x0FB0 */ s8 globalFlags[256]; + /* 0x10B0 */ s8 globalBytes[512]; + /* 0x12B0 */ s32 areaFlags[8]; + /* 0x12D0 */ s8 areaBytes[16]; + /* 0x12E0 */ char unk_12E0[6]; + /* 0x12E6 */ s16 savePos[3]; + /* 0x12EC */ s32 unk_12EC; + /* 0x12F0 */ s8 unk_12F0[12]; /* player name starts at 4th char */ + /* 0x12FC */ s32 unk_12FC; + /* 0x1300 */ s32 unk_1300; + /* 0x1304 */ char unk_1304[0x7C]; +} SaveData; // size = 0x1380 + #endif diff --git a/include/functions.h b/include/functions.h index 6754c373ec..f62d9d992a 100644 --- a/include/functions.h +++ b/include/functions.h @@ -10,8 +10,14 @@ void osCleanupThread(void); s32 heap_malloc(s32 size); HeapNode* _heap_create(void* addr, s32 size); +s8 get_global_byte(s32 index); +s32 get_global_flag(s32 index); +s8 get_area_byte(s32 index); +s32 get_area_flag(s32 index); + void clone_model(u16 srcModelID, u16 newModelID); Model* get_model_from_list_index(s32 listIndex); +void get_model_center_and_size(s32 modelID, f32* centerX, f32* centerY, f32* centerZ, f32* sizeX, f32* sizeY, f32* sizeZ); void update_collider_transform(s16 colliderID); void get_collider_center(s32 colliderID, f32* x, f32* y, f32* z); diff --git a/include/variables.h b/include/variables.h index f2078ce060..79dbce9b8c 100644 --- a/include/variables.h +++ b/include/variables.h @@ -92,4 +92,9 @@ extern UNK_TYPE D_80108558; extern UNK_TYPE D_8010F250; // play_sound state struct? extern s8 D_8010EBB0; +extern SaveData gCurrentSaveFile; + +extern u32* gMapFlags; +extern u32* gMapVars; + #endif diff --git a/src/code_a5dd0_len_114e0.c b/src/code_a5dd0_len_114e0.c index c154b19a60..a9577cdb92 100644 --- a/src/code_a5dd0_len_114e0.c +++ b/src/code_a5dd0_len_114e0.c @@ -191,7 +191,7 @@ INCLUDE_ASM("code_a5dd0_len_114e0", get_model_list_index_from_tree_index); INCLUDE_ASM("code_a5dd0_len_114e0", func_8011B090); -INCLUDE_ASM("code_a5dd0_len_114e0", get_model_center_and_size); +void INCLUDE_ASM("code_a5dd0_len_114e0", get_model_center_and_size, s32 modelID, f32* centerX, f32* centerY, f32* centerZ, f32* sizeX, f32* sizeY, f32* sizeZ); INCLUDE_ASM("code_a5dd0_len_114e0", func_8011B1C0); diff --git a/src/code_dba20_len_350.c b/src/code_dba20_len_350.c index 068ec82156..69b2579604 100644 --- a/src/code_dba20_len_350.c +++ b/src/code_dba20_len_350.c @@ -7,19 +7,104 @@ INCLUDE_ASM("code_dba20_len_350", clear_area_flags); INCLUDE_ASM("code_dba20_len_350", clear_global_flag); INCLUDE_ASM("code_dba20_len_350", set_global_flag); +/*s32 set_global_flag(s32 index) { + //SaveData* saveFile = &gCurrentSaveFile; + s32 flag; -INCLUDE_ASM("code_dba20_len_350", get_global_flag); + if (index <= -120000000) { + index += 130000000; + } -INCLUDE_ASM("code_dba20_len_350", set_global_byte); + flag = gCurrentSaveFile->globalFlags[index / 32] & (1 << (index % 32)); -INCLUDE_ASM("code_dba20_len_350", get_global_byte); + if (flag) { + flag = 1; + } + + gCurrentSaveFile->globalFlags[index / 32] |= (1 << (index % 32)); + return flag; +}*/ + +s32 INCLUDE_ASM("code_dba20_len_350", get_global_flag, s32 index); +/*s32 get_global_flag(s32 index) { + s32 bitIdx; + s32 wordIdx; + s32 bit; + s32 phi_return; + + if (index <= -120000000) { + index += 130000000; + } + wordIdx = index / 32; + bitIdx = index % 32; + bit = gCurrentSaveFile->globalFlags[wordIdx] & (1 << bitIdx); + + if (bit != 0) { + bit = 1; + } + return bit; + //return (bit != 0) ? 1 : bit; // ??? surely this is `bit != 0` +}*/ + +s8 set_global_byte(s32 index, s8 value) { + SaveData* saveFile = &gCurrentSaveFile; + s32 ret = saveFile->globalBytes[index]; + + saveFile->globalBytes[index] = value; + return ret; +} + +s8 get_global_byte(s32 index) { + return gCurrentSaveFile.globalBytes[index]; +} INCLUDE_ASM("code_dba20_len_350", clear_area_flag); INCLUDE_ASM("code_dba20_len_350", set_area_flag); +/*s32 set_area_flag(s32 index) { + SaveData* saveFile = &gCurrentSaveFile; + s32 flag; + s32 flagIdx; + s32 flagShift; -INCLUDE_ASM("code_dba20_len_350", get_area_flag); + flagIdx = index / 32; + flagShift = index % 32; -INCLUDE_ASM("code_dba20_len_350", set_area_byte); + flag = saveFile->areaFlags[flagIdx] & (1 << flagShift); -INCLUDE_ASM("code_dba20_len_350", get_area_byte); + if (flag != 0) { + flag = 1; + } + + saveFile->areaFlags[flagIdx] |= (1 << flagShift); + + return flag; +}*/ + +s32 get_area_flag(s32 index) { + s32 flag; + s32 flagIdx; + s32 flagShift; + + flagIdx = index / 32; + flagShift = index % 32; + + flag = gCurrentSaveFile.areaFlags[flagIdx] & (1 << flagShift); + + if (flag != 0) { + flag = 1; + } + return flag; +} + +s8 set_area_byte(s32 index, s8 value) { + SaveData* saveFile = &gCurrentSaveFile; + s32 ret = saveFile->areaBytes[index]; + + saveFile->areaBytes[index] = value; + return ret; +} + +s8 get_area_byte(s32 index) { + return gCurrentSaveFile.areaBytes[index]; +} diff --git a/src/code_e92d0_len_5da0.c b/src/code_e92d0_len_5da0.c index 064305180b..e21bad3142 100644 --- a/src/code_e92d0_len_5da0.c +++ b/src/code_e92d0_len_5da0.c @@ -719,6 +719,57 @@ s32 func_802C73B8(ScriptInstance* script) { INCLUDE_ASM("code_e92d0_len_5da0", si_execute_next_command); s32 INCLUDE_ASM("code_e92d0_len_5da0", get_variable, ScriptInstance* script, Bytecode var); +// TODO: consider renaming to si_get_variable +/*s32 get_variable(ScriptInstance* script, Bytecode var) { + s32 abs_value; + s32 word_index; + s32 bit_index; + + if (var <= -270000000) { + return var; + } else if (var <= -250000000) { + return var; + } else if (var <= -220000000) { + return (s32) fixed_var_to_float(var); + } else if (var <= -200000000) { + var += 210000000; + word_index = var / 32; + bit_index = var % 32; + return (script->flagArray[word_index] & (1 << bit_index)) != 0; + } else if (var <= -180000000) { + var += 190000000; + var = script->array[var]; + return (var > -270000000 && var < -220000000) ? (s32) fixed_var_to_float(var) : var; + } else if (var <= -160000000) { + return get_global_byte(var + 170000000); + } else if (var <= -140000000) { + return get_area_byte(var + 150000000); + } else if (var <= -120000000) { + return get_global_flag(var + 130000000); + } else if (var <= -100000000) { + return get_area_flag(var + 110000000); + } else if (var <= -80000000) { + s32 avar = var + 90000000; + word_index = avar / 32; + bit_index = avar % 32; + return (gMapFlags[word_index] & (1 << bit_index)) != 0; + } else if (var <= -60000000) { + var += 70000000; + word_index = var / 32; + bit_index = var % 32; + return (script->varFlags[word_index] & (1 << bit_index)) != 0; + } else if (var <= -40000000) { + var += 50000000; + var = gMapVars[var]; + return (var > -270000000 && var < -220000000) ? (s32) fixed_var_to_float(var) : var; + } else if (var <= -20000000) { + var += 30000000; + var = script->varTable[var]; + return (var > -270000000 && var < -220000000) ? (s32) fixed_var_to_float(var) : var; + } else { + return var; + } +}*/ INCLUDE_ASM("code_e92d0_len_5da0", get_variable_index); @@ -848,7 +899,21 @@ ApiStatus CloneModel(ScriptInstance* script, s32 isInitialCall) { return ApiStatus_DONE2; } -INCLUDE_API_ASM("code_e92d0_len_5da0", GetModelCenter); +ApiStatus GetModelCenter(ScriptInstance* script, s32 isInitialCall) { + Bytecode* thisPos = script->ptrReadPos; + f32 centerX; + f32 centerY; + f32 centerZ; + f32 sizeX; + f32 sizeY; + f32 sizeZ; + + get_model_center_and_size(get_variable(script, *thisPos++) & 0xFFFF, ¢erX, ¢erY, ¢erZ, &sizeX, &sizeY, &sizeZ); + script->varTable[0] = centerX; + script->varTable[1] = centerY; + script->varTable[2] = centerZ; + return ApiStatus_DONE2; +} INCLUDE_API_ASM("code_e92d0_len_5da0", SetTexPanner); @@ -856,7 +921,20 @@ INCLUDE_API_ASM("code_e92d0_len_5da0", SetModelFlag10); INCLUDE_API_ASM("code_e92d0_len_5da0", EnableTexPanning); -INCLUDE_API_ASM("code_e92d0_len_5da0", EnableModel); +ApiStatus EnableModel(ScriptInstance* script, s32 isInitialCall) { + Bytecode* thisPos = script->ptrReadPos; + Bytecode treeIndex = get_variable(script, *thisPos++); + s32 listIndex = get_model_list_index_from_tree_index(treeIndex); + Bytecode flag = get_variable(script, *thisPos++); + Model* model = get_model_from_list_index(listIndex); + + if (flag != 0) { + model->flags &= ~0x2; + } else { + model->flags |= 0x2; + } + return ApiStatus_DONE2; +} ApiStatus SetGroupEnabled(ScriptInstance* script, s32 isInitialCall) { Bytecode* thisPos = script->ptrReadPos; diff --git a/undefined_syms.txt b/undefined_syms.txt index c5a390091c..1956a2254b 100644 --- a/undefined_syms.txt +++ b/undefined_syms.txt @@ -112,6 +112,11 @@ gScriptIdList = 0x802D1898; gScriptIndexList = 0x802DAA98; gScriptListCount = 0x802DAC98; +gCurrentSaveFile = 0x800DACC0; + +gMapFlags = 0x802DA480; +gMapVars = 0x802DA484; + func_802A91F8 = 0x802A91F8; func_802A9208 = 0x802A9208; func_802A9228 = 0x802A9228;