// File: /workspace/github_cpp_makefile_star2_O2/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 0x257b
movq 0x8(%r14), %rsi
leaq 0x10(%rsp), %rbx
movq %rbx, %rdi
callq 0x2d38
movq 0x10(%r14), %r14
movq %r14, %rdi
callq 0x20a0
movq %rax, %rdi
movq %r14, %rsi
callq 0x238c
movq %rax, %r14
movl %r14d, (%rsp)
shrq $0x20, %r14
movb %r14b, 0x4(%rsp)
movzbl 0x40(%rsp), %edi
callq 0x2f8a
leaq 0x8(%rsp), %rcx
movq %rax, (%rcx)
leaq 0x48(%rsp), %rdi
movq %rcx, (%rdi)
movq %rbx, 0x8(%rdi)
movzbl %r14b, %eax
leaq 0x3709(%rip), %rcx      # 0x5c70
movq %rsp, %rsi
callq *(%rcx,%rax,8)
leaq 0x10(%rsp), %rdi
callq 0x2aa4
xorl %eax, %eax
jmp 0x25b3
movq 0x3a66(%rip), %rdi      # 0x5fe8
leaq 0x1a88(%rip), %rsi      # 0x4011
callq 0x2170
movq (%r14), %rsi
movq %rax, %rdi
callq 0x2170
leaq 0x1a79(%rip), %rsi      # 0x4019
movq %rax, %rdi
callq 0x2170
movq %rax, %rdi
callq 0x20d0
pushq $0x1
popq %rax
addq $0x58, %rsp
popq %rbx
popq %r14
retq
jmp 0x25bf
jmp 0x25bf
movq %rdx, %r14
movq %rax, %rbx
leaq 0x10(%rsp), %rdi
callq 0x2aa4
jmp 0x25d7
movq %rdx, %r14
movq %rax, %rbx
cmpl $0x1, %r14d
jne 0x2649
movq %rbx, %rdi
callq 0x2090
movq %rax, %rbx
movq 0x39f9(%rip), %rdi      # 0x5fe8
leaq 0x1a35(%rip), %rsi      # 0x402b
callq 0x2170
movq %rax, %rdi
callq 0x20d0
movq 0x39de(%rip), %rdi      # 0x5fe8
leaq 0x1a94(%rip), %rsi      # 0x40a5
callq 0x2170
movq %rax, %r14
movq (%rbx), %rax
movq %rbx, %rdi
callq *0x10(%rax)
movq %r14, %rdi
movq %rax, %rsi
callq 0x2170
movq %rax, %rdi
callq 0x20d0
callq 0x2210
pushq $0x2
jmp 0x25b2
movq %rax, %rbx
callq 0x2210
movq %rbx, %rdi
callq 0x2230
movq %rax, %rdi
callq 0x2aba
nop