from reccmp.compare.asm.instgen import InstructGen, SectionType
from reccmp.compare.asm.types import DisasmLiteInst


def test_ret():
    """Make sure we can handle a function with one instruction."""
    ig = InstructGen(b"\xc3", 0)
    assert len(ig.sections) == 1


SCORE_NOTIFY = (
    b"\x53\x56\x57\x8b\xd9\x33\xff\x8b\x74\x24\x10\x56\xe8\xbf\xe1\x01"
    b"\x00\x80\xbb\xf6\x00\x00\x00\x00\x0f\x84\x9c\x00\x00\x00\x8b\x4e"
    b"\x04\x49\x83\xf9\x17\x0f\x87\x8f\x00\x00\x00\x33\xc0\x8a\x81\xec"
    b"\x14\x00\x10\xff\x24\x85\xd4\x14\x00\x10\x8b\xcb\xbf\x01\x00\x00"
    b"\x00\xe8\x7a\x05\x00\x00\x8b\xc7\x5f\x5e\x5b\xc2\x04\x00\x56\x8b"
    b"\xcb\xe8\xaa\x00\x00\x00\x8b\xf8\x8b\xc7\x5f\x5e\x5b\xc2\x04\x00"
    b"\x80\x7e\x18\x20\x75\x07\x8b\xcb\xe8\xc3\xfe\xff\xff\xbf\x01\x00"
    b"\x00\x00\x8b\xc7\x5f\x5e\x5b\xc2\x04\x00\x56\x8b\xcb\xe8\x3e\x02"
    b"\x00\x00\x8b\xf8\x8b\xc7\x5f\x5e\x5b\xc2\x04\x00\x6a\x09\xa1\x4c"
    b"\x45\x0f\x10\x6a\x07\x50\xe8\x35\x45\x01\x00\x83\xc4\x0c\x8b\x83"
    b"\xf8\x00\x00\x00\x85\xc0\x74\x0d\x50\xe8\xa2\x42\x01\x00\x8b\xc8"
    b"\xe8\x9b\x9b\x03\x00\xbf\x01\x00\x00\x00\x8b\xc7\x5f\x5e\x5b\xc2"
    b"\x04\x00\x8b\xff\x4a\x14\x00\x10\x5e\x14\x00\x10\x70\x14\x00\x10"
    b"\x8a\x14\x00\x10\x9c\x14\x00\x10\xca\x14\x00\x10\x00\x01\x05\x05"
    b"\x05\x05\x02\x05\x05\x05\x05\x05\x05\x05\x05\x05\x03\x05\x05\x05"
    b"\x05\x05\x05\x04\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc"
)


def test_score_notify():
    """Score::Notify function from 0x10001410 in LEGO1.
    Good representative function for jump table (at 0x100014d4)
    and switch data (at 0x100014ec)."""
    ig = InstructGen(SCORE_NOTIFY, 0x10001410)

    # Did we get everything?
    assert len(ig.sections) == 3
    types_only = tuple(s.type for s in ig.sections)
    assert types_only == (SectionType.CODE, SectionType.ADDR_TAB, SectionType.DATA_TAB)

    # CODE section stopped at correct place?
    instructions = ig.sections[0].contents
    assert isinstance(instructions[-1], DisasmLiteInst)
    assert instructions[-1].address == 0x100014D2
    # n.b. 0x100014d2 is the dummy instruction `mov edi, edi`
    # Ghidra does more thorough analysis and ignores this.
    # The last real instruction should be at 0x100014cf. Not a big deal
    # to include this because it is not junk data.

    # 6 switch addresses
    assert len(ig.sections[1].contents) == 6

    # TODO: The data table at the end includes all of the 0xCC padding bytes.


SMACK_CASE = (
    # LEGO1: 0x100cdc43 (modified so jump table points at +0x1016)
    b"\x2e\xff\x24\x8d\x16\x10\x00\x00"
    # LEGO1: 0x100cdb62 (instructions before and after jump table)
    b"\x8b\xf8\xeb\x1a\x87\xdb\x87\xc9\x87\xdb\x87\xc9\x87\xdb\x50\xdc"
    b"\x0c\x10\xd0\xe2\x0c\x10\xb0\xe8\x0c\x10\x50\xe9\x0c\x10\xa0\x10"
    b"\x27\x10\x10\x3c\x11\x77\x17\x8a\xc8"
)


def test_smack_case():
    """Case where we have code / jump table / code.
    Need to properly separate code sections, eliminate junk instructions
    and continue disassembling at the proper address following the data."""
    ig = InstructGen(SMACK_CASE, 0x1000)
    assert len(ig.sections) == 3
    assert ig.sections[0].type == ig.sections[2].type == SectionType.CODE

    # Make sure we captured the instruction immediately after
    assert isinstance(ig.sections[2].contents[0], DisasmLiteInst)
    assert ig.sections[2].contents[0].mnemonic == "mov"


# BETA10 0x1004c9cc
BETA_FUNC = (
    b"\x55\x8b\xec\x83\xec\x08\x53\x56\x57\x89\x4d\xfc\x8b\x45\xfc\x33"
    b"\xc9\x8a\x88\x19\x02\x00\x00\x89\x4d\xf8\xe9\x1e\x00\x00\x00\xe9"
    b"\x41\x00\x00\x00\xe9\x3c\x00\x00\x00\xe9\x37\x00\x00\x00\xe9\x32"
    b"\x00\x00\x00\xe9\x2d\x00\x00\x00\xe9\x28\x00\x00\x00\x83\x7d\xf8"
    b"\x04\x0f\x87\x1e\x00\x00\x00\x8b\x45\xf8\xff\x24\x85\x1d\xca\x04"
    b"\x10\xeb\xc9\x04\x10\xf0\xc9\x04\x10\xf5\xc9\x04\x10\xfa\xc9\x04"
    b"\x10\xff\xc9\x04\x10\xb0\x01\xe9\x00\x00\x00\x00\x5f\x5e\x5b\xc9"
    b"\xc2\x04\x00"
)


def test_beta_case():
    """Complete (and short) function with CODE / ADDR / CODE"""
    ig = InstructGen(BETA_FUNC, 0x1004C9CC)
    # The JMP into the jump table immediately precedes the jump table.
    # We have to detect this and switch sections correctly or we will only
    # get 1 section.
    assert len(ig.sections) == 3
    assert ig.sections[0].type == ig.sections[2].type == SectionType.CODE

    # Make sure we captured the instruction immediately after
    assert isinstance(ig.sections[2].contents[0], DisasmLiteInst)
    assert ig.sections[2].contents[0].mnemonic == "mov"


# LEGO1 0x1000fb50
# TODO: The test data here is longer than it needs to be.
THUNK_TEST = (
    b"\x2b\x49\xfc\xe9\x08\x00\x00\x00\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc"
    b"\x56\x8b\xf1\xe8\xd8\xc5\x00\x00\x8b\xce\xe8\xb1\xdc\x01\x00\xf6"
    b"\x44\x24\x08\x01\x74\x0c\x8d\x46\xe0\x50\xe8\xe1\x66\x07\x00\x83"
    b"\xc4\x04\x8d\x46\xe0\x5e\xc2\x04\x00\xcc\xcc\xcc\xcc\xcc\xcc\xcc"
    b"\x2b\x49\xfc\xe9\x08\x00\x00\x00\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc"
    b"\xb8\x7c\x05\x0f\x10\xc3\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc"
    b"\x2b\x49\xfc\xe9\x08\x00\x00\x00\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc"
    b"\x8b\x54"
    # The problem is here: the last two bytes are the start of the next
    # function 0x1000fbc0. This is not enough data to read an instruction.
)


def test_thunk_case():
    """Adjuster thunk incorrectly annotated.
    We are reading way more bytes than we should for this function."""
    ig = InstructGen(THUNK_TEST, 0x1000FB50)
    # No switch cases here, so the only section is code.
    # This caused an infinite loop during testing so the goal is just to finish.
    assert len(ig.sections) == 1

    # TODO: We might detect the 0xCC padding bytes and cut off the function.
    # If we did that, we would correctly read only 2 instructions.
    # assert len(ig.sections[0].contents) == 2


# LEGO1 0x1006f080, Infocenter::HandleEndAction
HANDLE_END_ACTION = (
    b"\x53\x56\x57\x8b\xf1\x8b\x5c\x24\x10\x8b\x0d\x84\x45\x0f\x10\x8b"
    b"\x7b\x0c\x8b\x47\x20\x39\x01\x75\x29\x81\x7f\x1c\xf3\x01\x00\x00"
    b"\x75\x20\xe8\x59\x66\xfa\xff\x6a\x00\x8b\x40\x18\x6a\x00\x6a\x10"
    b"\x50\xff\x15\x38\xb5\x10\x10\xb8\x01\x00\x00\x00\x5f\x5e\x5b\xc2"
    b"\x04\x00\x39\x46\x0c\x0f\x85\xa2\x00\x00\x00\x8b\x47\x1c\x83\xf8"
    b"\x28\x74\x18\x83\xf8\x29\x74\x13\x83\xf8\x2a\x74\x0e\x83\xf8\x2b"
    b"\x74\x09\x83\xf8\x2c\x0f\x85\x82\x00\x00\x00\x66\x8b\x86\xd4\x01"
    b"\x00\x00\x66\x85\xc0\x74\x09\x66\x48\x66\x89\x86\xd4\x01\x00\x00"
    b"\x66\x83\xbe\xd4\x01\x00\x00\x00\x75\x63\x6a\x0b\xe8\xff\x67\xfa"
    b"\xff\x66\x8b\x86\xfc\x00\x00\x00\x83\xc4\x04\x50\xe8\x3f\x66\xfa"
    b"\xff\x8b\xc8\xe8\x58\xa6\xfc\xff\x0f\xbf\x86\xfc\x00\x00\x00\x48"
    b"\x83\xf8\x04\x77\x2f\xff\x24\x85\x78\xf4\x06\x10\x68\x1d\x02\x00"
    b"\x00\xeb\x1a\x68\x1e\x02\x00\x00\xeb\x13\x68\x1f\x02\x00\x00\xeb"
    b"\x0c\x68\x20\x02\x00\x00\xeb\x05\x68\x21\x02\x00\x00\x8b\xce\xe8"
    b"\x9c\x21\x00\x00\x6a\x01\x8b\xce\xe8\x53\x1c\x00\x00\x8d\x8e\x0c"
    b"\x01\x00\x00\x53\x8b\x01\xff\x50\x04\x85\xc0\x0f\x85\xef\x02\x00"
    b"\x00\x8b\x56\x0c\x8b\x4f\x20\x3b\xd1\x74\x0e\x8b\x1d\x74\x45\x0f"
    b"\x10\x39\x0b\x0f\x85\xd7\x02\x00\x00\x81\x7f\x1c\x02\x02\x00\x00"
    b"\x75\x1a\x6a\x00\x52\x6a\x10\xe8\xa4\x65\xfa\xff\x8b\xc8\xe8\x0d"
    b"\xa2\xfb\xff\x66\xc7\x86\xd6\x01\x00\x00\x00\x00\x8b\x96\x00\x01"
    b"\x00\x00\x8d\x42\x74\x8b\x18\x83\xfb\x0c\x0f\x87\x9b\x02\x00\x00"
    b"\x33\xc9\x8a\x8b\xac\xf4\x06\x10\xff\x24\x8d\x8c\xf4\x06\x10\x8b"
    b"\x86\x08\x01\x00\x00\x83\xf8\x05\x77\x07\xff\x24\x85\xbc\xf4\x06"
    b"\x10\x8b\xce\xe8\xb8\x1a\x00\x00\x8b\x86\x00\x01\x00\x00\x68\xf4"
    b"\x01\x00\x00\x8b\xce\xc7\x40\x74\x0b\x00\x00\x00\xe8\xef\x20\x00"
    b"\x00\x8b\x86\x00\x01\x00\x00\xc7\x86\x08\x01\x00\x00\xff\xff\xff"
    b"\xff\x83\x78\x78\x00\x0f\x85\x40\x02\x00\x00\xb8\x01\x00\x00\x00"
    b"\x5f\x66\xc7\x86\xd2\x01\x00\x00\x01\x00\x5e\x5b\xc2\x04\x00\x6a"
    b"\x00\x8b\xce\x6a\x01\xe8\xd6\x19\x00\x00\xb8\x01\x00\x00\x00\x5f"
    b"\x5e\x5b\xc2\x04\x00\x6a\x01\x8b\xce\x6a\x02\xe8\xc0\x19\x00\x00"
    b"\xb8\x01\x00\x00\x00\x5f\x5e\x5b\xc2\x04\x00\x8b\xce\xe8\x3e\x1a"
    b"\x00\x00\x8b\x86\x00\x01\x00\x00\x68\x1c\x02\x00\x00\x8b\xce\xc7"
    b"\x40\x74\x0b\x00\x00\x00\xe8\x75\x20\x00\x00\xb8\x01\x00\x00\x00"
    b"\x5f\xc7\x86\x08\x01\x00\x00\xff\xff\xff\xff\x5e\x5b\xc2\x04\x00"
    b"\x8b\xce\xe8\x09\x1a\x00\x00\x8b\x86\x00\x01\x00\x00\x68\x1b\x02"
    b"\x00\x00\x8b\xce\xc7\x40\x74\x0b\x00\x00\x00\xe8\x40\x20\x00\x00"
    b"\xb8\x01\x00\x00\x00\x5f\xc7\x86\x08\x01\x00\x00\xff\xff\xff\xff"
    b"\x5e\x5b\xc2\x04\x00\xc7\x00\x0b\x00\x00\x00\x8b\x86\x08\x01\x00"
    b"\x00\x83\xf8\x04\x74\x0c\x83\xf8\x05\x74\x0e\x68\xf4\x01\x00\x00"
    b"\xeb\x0c\x68\x1c\x02\x00\x00\xeb\x05\x68\x1b\x02\x00\x00\x8b\xce"
    b"\xe8\xfb\x1f\x00\x00\xb8\x01\x00\x00\x00\x5f\xc7\x86\x08\x01\x00"
    b"\x00\xff\xff\xff\xff\x5e\x5b\xc2\x04\x00\x6a\x00\xa1\xa0\x76\x0f"
    b"\x10\x50\xe8\x39\x65\xfa\xff\x83\xc4\x08\xa1\xa4\x76\x0f\x10\x6a"
    b"\x00\x50\xe8\x29\x65\xfa\xff\x83\xc4\x08\xe8\xf1\x63\xfa\xff\x8b"
    b"\xc8\xe8\x6a\x02\x01\x00\xb8\x01\x00\x00\x00\x5f\x5e\x5b\xc2\x04"
    b"\x00\x8b\x47\x1c\x83\xf8\x46\x74\x09\x83\xf8\x47\x0f\x85\x09\x01"
    b"\x00\x00\x6a\x00\x6a\x00\x6a\x32\x6a\x03\xe8\x91\x65\xfa\xff\x8b"
    b"\xc8\xe8\xfa\xc7\xfd\xff\x8b\x86\x00\x01\x00\x00\x5f\x5e\x5b\xc7"
    b"\x40\x74\x0e\x00\x00\x00\xb8\x01\x00\x00\x00\xc2\x04\x00\x8b\x47"
    b"\x1c\x39\x86\xf8\x00\x00\x00\x0f\x85\xce\x00\x00\x00\xe8\xbe\x63"
    b"\xfa\xff\x83\x78\x10\x02\x74\x19\x66\x8b\x86\xfc\x00\x00\x00\x66"
    b"\x85\xc0\x74\x0d\x50\xe8\xa6\x63\xfa\xff\x8b\xc8\xe8\xbf\xa3\xfc"
    b"\xff\x6a\x00\x6a\x00\x6a\x32\x6a\x03\xe8\x32\x65\xfa\xff\x8b\xc8"
    b"\xe8\x9b\xc7\xfd\xff\x8b\x86\x00\x01\x00\x00\x5f\x5e\x5b\xc7\x40"
    b"\x74\x0e\x00\x00\x00\xb8\x01\x00\x00\x00\xc2\x04\x00\x83\x7a\x78"
    b"\x00\x75\x32\x8b\x86\xf8\x00\x00\x00\x83\xf8\x28\x74\x27\x83\xf8"
    b"\x29\x74\x22\x83\xf8\x2a\x74\x1d\x83\xf8\x2b\x74\x18\x83\xf8\x2c"
    b"\x74\x13\x66\xc7\x86\xd0\x01\x00\x00\x01\x00\x6a\x0b\xe8\xee\x64"
    b"\xfa\xff\x83\xc4\x04\x8b\x86\x00\x01\x00\x00\x6a\x01\x68\xdc\x44"
    b"\x0f\x10\xc7\x40\x74\x02\x00\x00\x00\xe8\x22\x64\xfa\xff\x83\xc4"
    b"\x08\xb8\x01\x00\x00\x00\x5f\x5e\x5b\xc2\x04\x00\x8b\x47\x1c\x39"
    b"\x86\xf8\x00\x00\x00\x75\x14\x6a\x00\x6a\x00\x6a\x32\x6a\x03\xe8"
    b"\x9c\x64\xfa\xff\x8b\xc8\xe8\x05\xc7\xfd\xff\xb8\x01\x00\x00\x00"
    b"\x5f\x5e\x5b\xc2\x04\x00\x8b\xff\x3c\xf1\x06\x10\x43\xf1\x06\x10"
    b"\x4a\xf1\x06\x10\x51\xf1\x06\x10\x58\xf1\x06\x10\xdf\xf1\x06\x10"
    b"\xd5\xf2\x06\x10\x1a\xf3\x06\x10\x51\xf3\x06\x10\x8e\xf3\x06\x10"
    b"\xed\xf3\x06\x10\x4c\xf4\x06\x10\x6b\xf4\x06\x10\x00\x01\x02\x07"
    b"\x03\x04\x07\x07\x07\x07\x07\x05\x06\x8d\x49\x00\x3f\xf2\x06\x10"
    b"\x55\xf2\x06\x10\xf1\xf1\x06\x10\xf1\xf1\x06\x10\x6b\xf2\x06\x10"
    b"\xa0\xf2\x06\x10\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc"
)


def test_action_case():
    """3 switches: 3 jump tables, 1 data table"""
    ig = InstructGen(HANDLE_END_ACTION, 0x1006F080)
    # Two of the jump tables (0x1006f478 with 5, 0x1006f48c with 8)
    # are contiguous.
    assert len(ig.sections) == 5


# MFC 3.1, CDocument::ReportSaveLoadException
# HOVER: 0x451d73
REPORT_SAVE_LOAD_EXCEPTION = (
    b"\x64\xa1\x00\x00\x00\x00\x55\x8b\xec\x6a\xff\x68\xa3\x1e\x45\x00"
    b"\x50\x64\x89\x25\x00\x00\x00\x00\x81\xec\x10\x01\x00\x00\x56\x57"
    b"\x8b\x75\x14\x8b\x7d\x0c\x85\xff\x0f\x84\x8c\x00\x00\x00\x68\x58"
    b"\x6a\x4c\x00\x8b\xcf\xe8\x63\xc9\xff\xff\x85\xc0\x0f\x85\xdd\x00"
    b"\x00\x00\x68\x48\x6d\x4c\x00\x8b\xcf\xe8\x4f\xc9\xff\xff\x85\xc0"
    b"\x74\x19\x8b\x47\x08\x83\xf8\x03\x74\x0a\x83\xf8\x05\x7c\x5b\x83"
    b"\xf8\x07\x7f\x56\xbe\x20\xf1\x00\x00\xeb\x4f\x68\x38\x6f\x4c\x00"
    b"\x8b\xcf\xe8\x26\xc9\xff\xff\x85\xc0\x74\x3f\x8b\x47\x08\x48\x83"
    b"\xf8\x0c\x77\x36\x0f\xb6\x88\xd5\x1e\x45\x00\xff\x24\x8d\xb5\x1e"
    b"\x45\x00\x83\x7d\x10\x01\x1b\xf6\x81\xc6\x26\xf1\x00\x00\xeb\x1a"
    b"\xbe\x21\xf1\x00\x00\xeb\x13\x83\x7d\x10\x01\x1b\xf6\x81\xc6\x24"
    b"\xf1\x00\x00\xeb\x05\xbe\x22\xf1\x00\x00\x8d\x4d\xe8\xe8\x51\x9b"
    b"\xff\xff\xc7\x45\xfc\x00\x00\x00\x00\x83\x3d\x7c\x7f\x4b\x00\x00"
    b"\x68\x04\x01\x00\x00\x74\x11\x8d\x85\xe4\xfe\xff\xff\x50\xff\x75"
    b"\x08\xe8\xda\xd7\xff\xff\xeb\x0f\xff\x75\x08\x8d\x8d\xe4\xfe\xff"
    b"\xff\x51\xe8\xa8\x14\x00\x00\x8d\x85\xe4\xfe\xff\xff\x8d\x4d\xe8"
    b"\x50\x56\x51\xe8\x09\x54\x00\x00\x56\x6a\x30\xff\x75\xe8\xe8\x7f"
    b"\x29\x00\x00\xc7\x45\xfc\xff\xff\xff\xff\xe8\x1b\x00\x00\x00\x8b"
    b"\x45\xf4\x5f\x64\xa3\x00\x00\x00\x00\x5e\x8b\xe5\x5d\xc2\x10\x00"
    b"\xb8\x60\x1f\x4c\x00\xe9\x7b\xc1\xfe\xff\x8d\x4d\xe8\xe9\x3f\x9b"
    b"\xff\xff\x05\x1e\x45\x00\x13\x1e\x45\x00\x05\x1e\x45\x00\x1a\x1e"
    b"\x45\x00\x05\x1e\x45\x00\x05\x1e\x45\x00\x28\x1e\x45\x00\x2d\x1e"
    b"\x45\x00\x00\x01\x01\x02\x03\x04\x07\x05\x05\x05\x07\x07\x06"
)


def test_movzx_data_table():
    """Make sure we detect a data table accessed with a MOVZX instruction:
    00451df7 0f b6 88        MOVZX      this,byte ptr [EAX + 0x451ed5]=>PTR_caseD_7_00
             d5 1e 45 00
    """
    ig = InstructGen(REPORT_SAVE_LOAD_EXCEPTION, 0x451D73)
    assert len(ig.sections) == 3
    assert [section.type for section in ig.sections] == [
        SectionType.CODE,
        SectionType.ADDR_TAB,
        SectionType.DATA_TAB,
    ]
