// File: /workspace/github_cpp_makefile_star2_O3/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 %r14
pushq %rbx
subq $0x58, %rsp
movq %rsi, %r14
cmpl $0x3, %edi
jne 0x2724
movq 0x8(%r14), %rsi
leaq 0x10(%rsp), %rbx
movq %rbx, %rdi
callq 0x2e28
movq 0x10(%r14), %r14
movq %r14, %rdi
callq 0x20a0
movq %rax, %rdi
movq %r14, %rsi
callq 0x23cc
movq %rax, %r14
movl %r14d, (%rsp)
shrq $0x20, %r14
movb %r14b, 0x4(%rsp)
movzbl 0x40(%rsp), %edi
callq 0x308b
leaq 0x8(%rsp), %rcx
movq %rax, (%rcx)
leaq 0x48(%rsp), %rdi
movq %rcx, (%rdi)
movq %rbx, 0x8(%rdi)
movzbl %r14b, %eax
leaq 0x358f(%rip), %rcx      # 0x5c78
movq %rsp, %rsi
callq *(%rcx,%rax,8)
movq 0x28(%rsp), %rdi
testq %rdi, %rdi
je 0x2706
movq 0x38(%rsp), %rsi
subq %rdi, %rsi
callq 0x2190
movq 0x10(%rsp), %rdi
testq %rdi, %rdi
je 0x271d
movq 0x20(%rsp), %rsi
subq %rdi, %rsi
callq 0x2190
xorl %eax, %eax
jmp 0x27ba
movq 0x38bd(%rip), %rbx      # 0x5fe8
leaq 0x18df(%rip), %rsi      # 0x4011
movl $0x7, %edx
movq %rbx, %rdi
callq 0x21c0
movq (%r14), %r14
testq %r14, %r14
je 0x2763
movq %r14, %rdi
callq 0x20a0
movq 0x3892(%rip), %rdi      # 0x5fe8
movq %r14, %rsi
movq %rax, %rdx
callq 0x21c0
jmp 0x277a
movq (%rbx), %rax
movq -0x18(%rax), %rax
leaq (%rbx,%rax), %rdi
movl 0x20(%rbx,%rax), %esi
orl $0x1, %esi
callq 0x2250
leaq 0x1898(%rip), %rsi      # 0x4019
movl $0x11, %edx
movq %rbx, %rdi
callq 0x21c0
movq (%rbx), %rax
movq -0x18(%rax), %rdi
addq %rbx, %rdi
movl $0xa, %esi
callq 0x2160
movsbl %al, %esi
movq %rbx, %rdi
callq 0x2030
movq %rax, %rdi
callq 0x2110
movl $0x1, %eax
addq $0x58, %rsp
popq %rbx
popq %r14
retq
jmp 0x27c6
jmp 0x27c6
movq %rdx, %r14
movq %rax, %rbx
leaq 0x10(%rsp), %rdi
callq 0x2d54
jmp 0x27de
movq %rdx, %r14
movq %rax, %rbx
cmpl $0x1, %r14d
jne 0x2862
movq %rbx, %rdi
callq 0x2090
movq %rax, %rbx
movq 0x37f2(%rip), %rdi      # 0x5fe8
leaq 0x182e(%rip), %rsi      # 0x402b
movl $0x11, %edx
callq 0x21c0
movq 0x37da(%rip), %rdi      # 0x5fe8
callq 0x20b0
movq 0x37ce(%rip), %rdi      # 0x5fe8
leaq 0x1884(%rip), %rsi      # 0x40a5
movl $0x2, %edx
callq 0x21c0
movq (%rbx), %rax
movq %rbx, %rdi
callq *0x10(%rax)
movq 0x37ad(%rip), %rdi      # 0x5fe8
movq %rax, %rsi
callq 0x2170
movq %rax, %rdi
callq 0x20b0
callq 0x2240
movl $0x2, %eax
jmp 0x27ba
movq %rax, %rbx
callq 0x2240
movq %rbx, %rdi
callq 0x2270
movq %rax, %rdi
callq 0x2d84