// File: StanHash[P]nesdump/nesdump.cc
// Function: main
int main(int argc, char** argv)
{
    if (argc != 3)
    {
        std::cerr << "Usage: " << argv[0] << " <rom.nes> <item>" << std::endl;
        return 1;
    }

    try
    {
        NesRom rom = NesRom::from_ines_file(argv[1]);
        Item item = parse_item(argv[2]);

        MapperInfo const* const mapper_info = get_mapper_info(rom.get_mapper_id());

        std::visit([&] (auto& item)
        {
            using T = std::decay_t<decltype(item)>;

            if constexpr (std::is_same_v<T, ItemInfo>)
            {
                std::cout << "ROM Info:" << std::endl;
                std::cout << "  Mapper:       " << mapper_info->name << " (" << std::dec << (unsigned) rom.get_mapper_id() << ")" << std::endl;
                std::cout << "  iNES flags:   " << std::hex << (unsigned) rom.get_ines_flags() << std::endl;
                std::cout << "  PRG-ROM size: " << std::dec << rom.get_prg_rom().size()/1024 << "K (" << std::dec << mapper_info->count_prg_banks(rom) << " bank(s))" << std::endl;
                std::cout << "  CHR-ROM size: " << std::dec << rom.get_chr_rom().size()/1024 << "K (" << std::dec << mapper_info->count_chr_banks(rom) << " bank(s))" << std::endl;
            }

            if constexpr (std::is_same_v<T, ItemPrg>)
            {
                int bank = item.bank;

                if (bank < 0)
                {
                    // Write all
                    std::cout.write(reinterpret_cast<char const*>(rom.get_prg_rom().data()), rom.get_prg_rom().size());
                }
                else
                {
                    if (mapper_info->prg_bank_size == 0)
                        throw std::runtime_error("This mapper isn't known to feature PRG-ROM banking");

                    if ((unsigned) bank >= mapper_info->count_prg_banks(rom))
                        throw std::runtime_error("Bank number is too high");

                    std::cout.write(reinterpret_cast<char const*>(mapper_info->get_prg_bank(rom, bank)), mapper_info->prg_bank_size);
                }
            }

            if constexpr (std::is_same_v<T, ItemChr>)
            {
                int bank = item.bank;

                if (bank < 0)
                {
                    // Write all
                    std::cout.write(reinterpret_cast<char const*>(rom.get_chr_rom().data()), rom.get_chr_rom().size());
                }
                else
                {
                    if (mapper_info->chr_bank_size == 0)
                        throw std::runtime_error("This mapper isn't known to feature CHR-ROM banking");

                    if ((unsigned) bank >= mapper_info->count_chr_banks(rom))
                        throw std::runtime_error("Bank number is too high");

                    std::cout.write(reinterpret_cast<char const*>(mapper_info->get_chr_bank(rom, bank)), mapper_info->chr_bank_size);
                }
            }
        }, item);
    }
    catch (std::runtime_error const& e)
    {
        std::cerr << "An error occured:" << std::endl;
        std::cerr << "  " << e.what() << std::endl;

        return 2;
    }
}

// Assembly:
pushq %rbp
movq %rsp, %rbp
subq $0xc0, %rsp
movl $0x0, -0x4(%rbp)
movl %edi, -0x8(%rbp)
movq %rsi, -0x10(%rbp)
cmpl $0x3, -0x8(%rbp)
je 0x274b
movq 0x78e2(%rip), %rdi      # 0x9fe8
leaq 0x3904(%rip), %rsi      # 0x6011
callq 0x2190
movq %rax, %rdi
movq -0x10(%rbp), %rax
movq (%rax), %rsi
callq 0x2190
movq %rax, %rdi
leaq 0x38ee(%rip), %rsi      # 0x6019
callq 0x2190
movq %rax, %rdi
movq 0x7866(%rip), %rsi      # 0x9fa0
callq 0x21c0
movl $0x1, -0x4(%rbp)
jmp 0x28ee
movq -0x10(%rbp), %rax
movq 0x8(%rax), %rsi
leaq -0x48(%rbp), %rdi
callq 0x4c50
jmp 0x275e
movq -0x10(%rbp), %rax
movq 0x10(%rax), %rsi
leaq -0x70(%rbp), %rdi
callq 0x3360
movq -0x70(%rbp), %rdi
movq -0x68(%rbp), %rsi
callq 0x2430
movq %rax, -0x98(%rbp)
jmp 0x2785
movq -0x98(%rbp), %rax
movl %eax, %ecx
movl %ecx, -0x5c(%rbp)
shrq $0x20, %rax
movb %al, -0x58(%rbp)
leaq -0x48(%rbp), %rdi
callq 0x3580
movb %al, -0x99(%rbp)
jmp 0x27a9
movb -0x99(%rbp), %al
movzbl %al, %edi
callq 0x5240
movq %rax, -0xa8(%rbp)
jmp 0x27c0
movq -0xa8(%rbp), %rax
movq %rax, -0x78(%rbp)
leaq -0x78(%rbp), %rax
movq %rax, -0x88(%rbp)
leaq -0x48(%rbp), %rax
movq %rax, -0x80(%rbp)
leaq -0x88(%rbp), %rdi
leaq -0x5c(%rbp), %rsi
callq 0x2910
jmp 0x27f0
leaq -0x48(%rbp), %rdi
callq 0x35a0
jmp 0x28ee
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x50(%rbp)
movl %eax, -0x54(%rbp)
jmp 0x2821
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x50(%rbp)
movl %eax, -0x54(%rbp)
leaq -0x48(%rbp), %rdi
callq 0x35a0
movl -0x54(%rbp), %eax
movl $0x1, %ecx
cmpl %ecx, %eax
jne 0x28fa
movq -0x50(%rbp), %rdi
callq 0x20b0
movq %rax, -0x90(%rbp)
movq 0x77a0(%rip), %rdi      # 0x9fe8
leaq 0x37dc(%rip), %rsi      # 0x602b
callq 0x2190
movq %rax, -0xb0(%rbp)
jmp 0x285d
movq -0xb0(%rbp), %rdi
movq 0x7735(%rip), %rsi      # 0x9fa0
callq 0x21c0
jmp 0x2872
movq 0x776f(%rip), %rdi      # 0x9fe8
leaq 0x3846(%rip), %rsi      # 0x60c6
callq 0x2190
movq %rax, -0xb8(%rbp)
jmp 0x288e
movq -0x90(%rbp), %rdi
movq (%rdi), %rax
movq 0x10(%rax), %rax
callq *%rax
movq -0xb8(%rbp), %rdi
movq %rax, %rsi
callq 0x2190
movq %rax, -0xc0(%rbp)
jmp 0x28b6
movq -0xc0(%rbp), %rdi
movq 0x76dc(%rip), %rsi      # 0x9fa0
callq 0x21c0
jmp 0x28cb
movl $0x2, -0x4(%rbp)
callq 0x2290
jmp 0x28ee
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x50(%rbp)
movl %eax, -0x54(%rbp)
callq 0x2290
jmp 0x28ec
jmp 0x28fa
movl -0x4(%rbp), %eax
addq $0xc0, %rsp
popq %rbp
retq
movq -0x50(%rbp), %rdi
callq 0x22b0
movq %rax, %rdi
callq 0x35d0
nopl (%rax,%rax)