// File: /workspace/github_cpp_makefile_star2_O2/Thilotilo[P]DoaERando/Randomizer.cpp
// Function: DoaERando::Randomizer::CaveShuffle()
void Randomizer::CaveShuffle()
{
    // These are world map offsets
    const int XU_ZHE_CAVE_OFFSET = 0x07;
    const int MT_GANG_TAI_ENTRANCE_OFFSET = 0x0B;
    const int MT_GANG_TAI_EXIT_OFFSET = 0x1F;
    const int WU_CAVE_EXIT_OFFSET = 0x26;
    const int WU_CAVE_ENTRANCE_OFFSET = 0x39;
    const int GOLD_KEY_CAVE_OFFSET = 0x46;
    const int CHANG_AN_ENTRANCE_OFFSET = 0x58;
    const int CHANG_AN_EXIT_OFFSET = 0x61;
    const int NAN_YANG_CAVE_OFFSET = 0x63;
    const int WEI_CAVE_2_ENTRANCE_OFFSET = 0x6C;
    const int WEI_CAVE_2_EXIT_OFFSET = 0x7B;
    const int WEI_CAVE_1_EXIT_OFFSET = 0x7E;
    const int WEI_CAVE_1_ENTRANCE_OFFSET = 0x93;
    const int SALTPETER_CAVE_OFFSET = 0x99;

    // Magic numbers are bad, but 8 million consts to start a method are obnoxious.
    vector<vector<BYTE>> CaveWarpOffsets = {
        {XU_ZHE_CAVE_OFFSET,          0x08, 0x09},
        {MT_GANG_TAI_ENTRANCE_OFFSET, 0x0A},
        {MT_GANG_TAI_EXIT_OFFSET,     0x1E},
        {WU_CAVE_EXIT_OFFSET,         0x27},
        {WU_CAVE_ENTRANCE_OFFSET,     0x38},
        {GOLD_KEY_CAVE_OFFSET,        0x47, 0x48},
        {CHANG_AN_ENTRANCE_OFFSET,    0x57, 0x59},
        {CHANG_AN_EXIT_OFFSET,        0x60, 0x62},
        {NAN_YANG_CAVE_OFFSET,        0x64, 0x65},
        {WEI_CAVE_2_ENTRANCE_OFFSET,  0x6D, 0x7D},
        {WEI_CAVE_2_EXIT_OFFSET,      0x7A, 0x7C},
        {WEI_CAVE_1_EXIT_OFFSET,      0x7F, 0x80},
        {WEI_CAVE_1_ENTRANCE_OFFSET,  0x94, 0x95},
        {SALTPETER_CAVE_OFFSET,       0x9A, 0x9B}
    };

    vector<vector<BYTE>> ShuffledOffsets = CaveWarpOffsets;

    for (int i = 0; i < 10000; ++i)
    {
        int location1 = myRNG.GetRandomInt(0, ShuffledOffsets.size() - 1);
        int location2 = myRNG.GetRandomInt(0, ShuffledOffsets.size() - 1);
        swap(ShuffledOffsets[location1], ShuffledOffsets[location2]);
    }

    // Now do sanity checks
    // For now, let's prevent the following:
    // 1) Wu Cave entrance should not be a single-entrance cave to prevent softlocks from canal
    // 2) Mt Gang Tai Exit cannot be a single-entrance cave, and it's other side cannot come to
    //    one of the Wei caves if the other is a Single Exit cave
    // 3) Wei1 Exit & Wei2 Entrance can't be opposite ends of the same cave
    // 4) Wei1 Exit & Wei2 Entrance can't both be single-entrance caves.
    while (IsSingleExitCave(ShuffledOffsets[4][0]) ||
           IsSingleExitCave(ShuffledOffsets[2][0]) ||
           (AreExitsFromSameCave(ShuffledOffsets[2][0], ShuffledOffsets[9][0]) &&
            IsSingleExitCave(ShuffledOffsets[11][0])) ||
           (AreExitsFromSameCave(ShuffledOffsets[2][0], ShuffledOffsets[11][0]) &&
            IsSingleExitCave(ShuffledOffsets[9][0])) ||
           AreExitsFromSameCave(ShuffledOffsets[9][0], ShuffledOffsets[11][0]) ||
           (IsSingleExitCave(ShuffledOffsets[9][0]) && IsSingleExitCave(ShuffledOffsets[11][0])))
    {
        int location1 = myRNG.GetRandomInt(0, ShuffledOffsets.size() - 1);
        int location2 = myRNG.GetRandomInt(0, ShuffledOffsets.size() - 1);
        swap(ShuffledOffsets[location1], ShuffledOffsets[location2]);
    }

    // Collect all the bytes we need to update
    std::vector<std::vector<BYTE>> bytesToWrite;
    for (int i = 0; i < ShuffledOffsets.size(); ++i)
    {
        printf("Putting cave id 0x%02X in the vanilla 0x%02X location\n",
               ShuffledOffsets[i][0], CaveWarpOffsets[i][0]);
        int lsb = myRom->ReadByte(0x1C210 + ShuffledOffsets[i][0]);
        int msb = myRom->ReadByte(0x1C2C0 + ShuffledOffsets[i][0]);
        int shuffledAddressInBank = (msb << 8) | lsb;

        lsb = myRom->ReadByte(0x1C210 + CaveWarpOffsets[i][1]);
        msb = myRom->ReadByte(0x1C2C0 + CaveWarpOffsets[i][1]);
        int caveExitAddressInBank = (msb << 8) | lsb;

        // This puts the destination of the shuffled world map address into the vanilla location
        std::vector<BYTE> updateToWrite;
        updateToWrite.push_back(CaveWarpOffsets[i][0]);
        for (int j = 4; j < 9; ++j)
        {
            updateToWrite.push_back(myRom->ReadByteAtBank(0x7, shuffledAddressInBank + j));
        }

        bytesToWrite.push_back(updateToWrite);

        // This puts the destination of the vanilla cave exits to the shuffled world map location
        for (int j = 1; j < ShuffledOffsets[i].size(); ++j)
        {
            std::vector<BYTE> updatedCaveEntrance;
            updatedCaveEntrance.push_back(ShuffledOffsets[i][j]);
            for (int k = 4; k < 9; ++k)
            {
                updatedCaveEntrance.push_back(myRom->ReadByteAtBank(0x7, caveExitAddressInBank + k));
            }
            bytesToWrite.push_back(updatedCaveEntrance);
        }
    }

    // Now write everything
    for (auto& entry : bytesToWrite)
    {
        int lsb = myRom->ReadByte(0x1C210 + entry[0]);
        int msb = myRom->ReadByte(0x1C2C0 + entry[0]);
        int addressInBank = (msb << 8) | lsb;

        for (int i = 1; i < entry.size(); ++i)
        {
            myRom->WriteByteAtBank(0x7, addressInBank + i + 3, entry[i]);
        }
    }
}

// Assembly:
pushq %rbp
movq %rsp, %rbp
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
subq $0x238, %rsp            # imm = 0x238
movq %rdi, %rbx
leaq -0xb0(%rbp), %rsi
movw $0x807, (%rsi)          # imm = 0x807
movb $0x9, 0x2(%rsi)
leaq -0x260(%rbp), %r14
pushq $0x3
popq %rdx
leaq -0x4a(%rbp), %rcx
movq %r14, %rdi
callq 0x409c64
leaq -0x248(%rbp), %r15
leaq -0x70(%rbp), %rsi
movw $0xa0b, (%rsi)          # imm = 0xA0B
pushq $0x2
popq %rdx
leaq -0x49(%rbp), %rcx
movq %r15, %rdi
callq 0x409c64
leaq -0x230(%rbp), %r15
leaq -0x6e(%rbp), %rsi
movw $0x1e1f, (%rsi)         # imm = 0x1E1F
pushq $0x2
popq %rdx
leaq -0x48(%rbp), %rcx
movq %r15, %rdi
callq 0x409c64
leaq -0x218(%rbp), %r15
leaq -0x6c(%rbp), %rsi
movw $0x2726, (%rsi)         # imm = 0x2726
pushq $0x2
popq %rdx
leaq -0x47(%rbp), %rcx
movq %r15, %rdi
callq 0x409c64
leaq -0x200(%rbp), %r15
leaq -0x6a(%rbp), %rsi
movw $0x3839, (%rsi)         # imm = 0x3839
pushq $0x2
popq %rdx
leaq -0x46(%rbp), %rcx
movq %r15, %rdi
callq 0x409c64
leaq -0x1e8(%rbp), %r15
leaq -0x90(%rbp), %rsi
movw $0x4746, (%rsi)         # imm = 0x4746
movb $0x48, 0x2(%rsi)
pushq $0x3
popq %rdx
leaq -0x45(%rbp), %rcx
movq %r15, %rdi
callq 0x409c64
leaq -0x1d0(%rbp), %r15
leaq -0x100(%rbp), %rsi
movw $0x5758, (%rsi)         # imm = 0x5758
movb $0x59, 0x2(%rsi)
pushq $0x3
popq %rdx
leaq -0x44(%rbp), %rcx
movq %r15, %rdi
callq 0x409c64
leaq -0x1b8(%rbp), %r15
leaq -0x43(%rbp), %rsi
movw $0x6061, (%rsi)         # imm = 0x6061
movb $0x62, 0x2(%rsi)
pushq $0x3
popq %rdx
leaq -0x40(%rbp), %rcx
movq %r15, %rdi
callq 0x409c64
leaq -0x1a0(%rbp), %r15
leaq -0xda(%rbp), %rsi
movw $0x6463, (%rsi)         # imm = 0x6463
movb $0x65, 0x2(%rsi)
pushq $0x3
popq %rdx
leaq -0x3f(%rbp), %rcx
movq %r15, %rdi
callq 0x409c64
leaq -0x188(%rbp), %r15
leaq -0xd7(%rbp), %rsi
movw $0x6d6c, (%rsi)         # imm = 0x6D6C
movb $0x7d, 0x2(%rsi)
pushq $0x3
popq %rdx
leaq -0x3e(%rbp), %rcx
movq %r15, %rdi
callq 0x409c64
leaq -0x170(%rbp), %r15
leaq -0xd4(%rbp), %rsi
movw $0x7a7b, (%rsi)         # imm = 0x7A7B
movb $0x7c, 0x2(%rsi)
pushq $0x3
popq %rdx
leaq -0x3d(%rbp), %rcx
movq %r15, %rdi
callq 0x409c64
leaq -0x158(%rbp), %r15
leaq -0xd1(%rbp), %rsi
movw $0x7f7e, (%rsi)         # imm = 0x7F7E
movb $-0x80, 0x2(%rsi)
pushq $0x3
popq %rdx
leaq -0x3c(%rbp), %rcx
movq %r15, %rdi
callq 0x409c64
leaq -0x140(%rbp), %r15
leaq -0xce(%rbp), %rsi
movw $0x9493, (%rsi)         # imm = 0x9493
movb $-0x6b, 0x2(%rsi)
pushq $0x3
popq %rdx
leaq -0x3b(%rbp), %rcx
movq %r15, %rdi
callq 0x409c64
leaq -0x128(%rbp), %r15
leaq -0xcb(%rbp), %rsi
movw $0x9a99, (%rsi)         # imm = 0x9A99
movb $-0x65, 0x2(%rsi)
pushq $0x3
popq %rdx
leaq -0x3a(%rbp), %rcx
movq %r15, %rdi
callq 0x409c64
movq %rbx, -0x58(%rbp)
leaq -0xc8(%rbp), %rdi
leaq -0x260(%rbp), %rsi
pushq $0xe
popq %rdx
leaq -0x39(%rbp), %rcx
callq 0x409c9a
movl $0x138, %ebx            # imm = 0x138
leaq (%rbx,%rbp), %rdi
addq $-0x260, %rdi           # imm = 0xFDA0
callq 0x404baa
addq $-0x18, %rbx
cmpq $-0x18, %rbx
jne 0x4052f9
leaq -0x260(%rbp), %rdi
leaq -0xc8(%rbp), %rsi
callq 0x409cd4
movq -0x58(%rbp), %rbx
leaq 0x8(%rbx), %r14
movl $0x2710, %r12d          # imm = 0x2710
pushq $0x18
popq %r13
subl $0x1, %r12d
jb 0x4053a2
movq -0x258(%rbp), %rax
subq -0x260(%rbp), %rax
cqto
idivq %r13
leal -0x1(%rax), %edx
movq %r14, %rdi
xorl %esi, %esi
callq 0x40ed8e
movl %eax, %r15d
movq -0x258(%rbp), %rax
subq -0x260(%rbp), %rax
cqto
idivq %r13
leal -0x1(%rax), %edx
movq %r14, %rdi
xorl %esi, %esi
callq 0x40ed8e
movslq %r15d, %rcx
movq -0x260(%rbp), %rdx
imulq $0x18, %rcx, %rdi
addq %rdx, %rdi
cltq
imulq $0x18, %rax, %rsi
addq %rdx, %rsi
callq 0x404dd6
jmp 0x405338
movq %r14, -0x38(%rbp)
movq -0x260(%rbp), %r12
movq 0x60(%r12), %rax
movzbl (%rax), %edi
callq 0x4058b0
testb %al, %al
jne 0x40544c
movq 0x30(%r12), %rax
movzbl (%rax), %r13d
movl %r13d, %edi
callq 0x4058b0
testb %al, %al
jne 0x40544c
movq 0xd8(%r12), %rax
movzbl (%rax), %r14d
movl %r13d, %edi
movl %r14d, %esi
callq 0x4058d1
movq 0x108(%r12), %rcx
movzbl (%rcx), %r15d
testb %al, %al
je 0x40540a
movl %r15d, %edi
callq 0x4058b0
testb %al, %al
jne 0x40544c
movl %r13d, %edi
movl %r15d, %esi
callq 0x4058d1
testb %al, %al
je 0x405425
movl %r14d, %edi
callq 0x4058b0
testb %al, %al
jne 0x40544c
movl %r14d, %edi
movl %r15d, %esi
callq 0x4058d1
testb %al, %al
jne 0x40544c
movl %r14d, %edi
callq 0x4058b0
testb %al, %al
je 0x4054b7
movl %r15d, %edi
callq 0x4058b0
testb %al, %al
je 0x4054b7
movq -0x258(%rbp), %rax
subq %r12, %rax
cqto
pushq $0x18
popq %r12
idivq %r12
leal -0x1(%rax), %edx
movq -0x38(%rbp), %r14
movq %r14, %rdi
xorl %esi, %esi
callq 0x40ed8e
movl %eax, %r15d
movq -0x258(%rbp), %rax
subq -0x260(%rbp), %rax
cqto
idivq %r12
leal -0x1(%rax), %edx
movq %r14, %rdi
xorl %esi, %esi
callq 0x40ed8e
movslq %r15d, %rcx
movq -0x260(%rbp), %rdx
imulq $0x18, %rcx, %rdi
addq %rdx, %rdi
cltq
imulq $0x18, %rax, %rsi
addq %rdx, %rsi
callq 0x404dd6
jmp 0x4053a6
xorps %xmm0, %xmm0
leaq -0xb0(%rbp), %rax
andq $0x0, 0x10(%rax)
movaps %xmm0, (%rax)
leaq -0x100(%rbp), %r13
pushq $0x7
popq %r15
leaq -0x43(%rbp), %r14
xorl %edi, %edi
movq %r12, %rsi
movq -0x258(%rbp), %rax
subq %rsi, %rax
cqto
pushq $0x18
popq %rcx
idivq %rcx
cmpq %rdi, %rax
jbe 0x40570c
movq %rdi, -0x108(%rbp)
imulq $0x18, %rdi, %r12
movq (%rsi,%r12), %rax
movzbl (%rax), %esi
movq -0xc8(%rbp), %rax
movq (%rax,%r12), %rax
movzbl (%rax), %edx
leaq 0x185bda(%rip), %rdi    # 0x58b0f9
xorl %eax, %eax
callq 0x4e1140
movq (%rbx), %rdi
movq -0x260(%rbp), %rax
movq (%rax,%r12), %rax
movzbl (%rax), %esi
addl $0x1c210, %esi          # imm = 0x1C210
callq 0x4049d6
movb %al, -0x38(%rbp)
movq (%rbx), %rdi
movq -0x260(%rbp), %rax
movq (%rax,%r12), %rax
movzbl (%rax), %esi
addl $0x1c2c0, %esi          # imm = 0x1C2C0
callq 0x4049d6
movb %al, -0x68(%rbp)
movq (%rbx), %rdi
movq -0xc8(%rbp), %rax
movq (%rax,%r12), %rax
movzbl 0x1(%rax), %esi
addl $0x1c210, %esi          # imm = 0x1C210
callq 0x4049d6
movb %al, -0x60(%rbp)
movq (%rbx), %rdi
movq -0xc8(%rbp), %rax
movq (%rax,%r12), %rax
movzbl 0x1(%rax), %esi
addl $0x1c2c0, %esi          # imm = 0x1C2C0
callq 0x4049d6
movb %al, -0x29(%rbp)
andq $0x0, -0x80(%rbp)
xorps %xmm0, %xmm0
movaps %xmm0, -0x90(%rbp)
movq -0xc8(%rbp), %rax
movq %r12, -0x110(%rbp)
movq (%rax,%r12), %rsi
leaq -0x90(%rbp), %rdi
callq 0x409c42
movzbl -0x38(%rbp), %eax
movzbl -0x68(%rbp), %ecx
shll $0x8, %ecx
movzbl -0x60(%rbp), %edx
movq %rdx, -0x68(%rbp)
movzbl -0x29(%rbp), %edx
shll $0x8, %edx
movq %rdx, -0x60(%rbp)
addl %ecx, %eax
addl $0x4, %eax
movq %rax, -0x38(%rbp)
xorl %r12d, %r12d
cmpl $0x5, %r12d
je 0x40562e
movq (%rbx), %rdi
movq -0x38(%rbp), %rax
leal (%rax,%r12), %edx
movl %r15d, %esi
callq 0x4049e6
movb %al, -0x100(%rbp)
leaq -0x90(%rbp), %rdi
movq %r13, %rsi
callq 0x409faa
incl %r12d
jmp 0x4055fb
leaq -0xb0(%rbp), %rdi
leaq -0x90(%rbp), %rsi
callq 0x409d2e
movq -0x68(%rbp), %rax
movq -0x60(%rbp), %rcx
leal (%rcx,%rax), %r12d
addl $0x4, %r12d
pushq $0x1
popq %rdi
movq -0x260(%rbp), %rax
movq -0x110(%rbp), %rdx
movq 0x8(%rax,%rdx), %rcx
subq (%rax,%rdx), %rcx
cmpq %rdi, %rcx
jbe 0x4056ea
andq $0x0, -0xf0(%rbp)
addq %rdx, %rax
xorps %xmm0, %xmm0
movaps %xmm0, -0x100(%rbp)
movq (%rax), %rsi
movq %rdi, -0x38(%rbp)
addq %rdi, %rsi
movq %r13, %rdi
callq 0x409c42
xorl %ebx, %ebx
cmpl $0x5, %ebx
je 0x4056c3
movq -0x58(%rbp), %rax
movq (%rax), %rdi
leal (%r12,%rbx), %edx
movl %r15d, %esi
callq 0x4049e6
movb %al, -0x43(%rbp)
movq %r13, %rdi
movq %r14, %rsi
callq 0x409faa
incl %ebx
jmp 0x405699
leaq -0xb0(%rbp), %rdi
movq %r13, %rsi
callq 0x409d2e
movq %r13, %rdi
callq 0x404baa
movq -0x38(%rbp), %rdi
incq %rdi
movq -0x58(%rbp), %rbx
jmp 0x405654
leaq -0x90(%rbp), %rdi
callq 0x404baa
movq -0x108(%rbp), %rdi
incq %rdi
movq -0x260(%rbp), %rsi
jmp 0x4054dd
movq -0xb0(%rbp), %r12
movq -0xa8(%rbp), %r13
pushq $0x7
popq %r14
cmpq %r13, %r12
je 0x40579e
movq (%rbx), %rdi
movq (%r12), %rax
movzbl (%rax), %esi
addl $0x1c210, %esi          # imm = 0x1C210
callq 0x4049d6
movl %eax, %r15d
movq (%rbx), %rdi
movq (%r12), %rax
movzbl (%rax), %esi
addl $0x1c2c0, %esi          # imm = 0x1C2C0
callq 0x4049d6
movzbl %al, %eax
shll $0x8, %eax
movzbl %r15b, %ecx
leal (%rax,%rcx), %r15d
addl $0x4, %r15d
pushq $0x1
popq %rbx
movq (%r12), %rax
movq 0x8(%r12), %rcx
subq %rax, %rcx
cmpq %rbx, %rcx
jbe 0x405794
movq -0x58(%rbp), %rcx
movq (%rcx), %rdi
movzbl (%rax,%rbx), %ecx
movl %r14d, %esi
movl %r15d, %edx
callq 0x404a10
incq %rbx
incl %r15d
jmp 0x405765
addq $0x18, %r12
movq -0x58(%rbp), %rbx
jmp 0x40571e
leaq -0xb0(%rbp), %rdi
callq 0x409d6a
leaq -0x260(%rbp), %rdi
callq 0x409d6a
leaq -0xc8(%rbp), %rdi
callq 0x409d6a
addq $0x238, %rsp            # imm = 0x238
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
movq %rax, %rbx
jmp 0x40589c
movq %rax, %rbx
movl $0x138, %r12d           # imm = 0x138
leaq (%r12,%rbp), %rdi
addq $-0x260, %rdi           # imm = 0xFDA0
callq 0x404baa
addq $-0x18, %r12
cmpq $-0x18, %r12
jne 0x4057e5
movb $0x1, %al
jmp 0x405820
jmp 0x40581b
jmp 0x40581b
jmp 0x40581b
jmp 0x40581b
jmp 0x40581b
jmp 0x40581b
jmp 0x40581b
jmp 0x40581b
jmp 0x40581b
jmp 0x40581b
jmp 0x40581b
jmp 0x40581b
movq %rax, %rbx
xorl %eax, %eax
cmpq %r15, %r14
sete %cl
testb %al, %al
jne 0x4058a8
testb %cl, %cl
jne 0x4058a8
addq $-0x18, %r15
movq %r15, %rdi
callq 0x404baa
cmpq %r14, %r15
jne 0x40582e
jmp 0x4058a8
movq %rax, %rbx
jmp 0x4058a8
jmp 0x40585d
jmp 0x40585d
jmp 0x40585d
jmp 0x40585d
jmp 0x405858
jmp 0x405858
jmp 0x405864
jmp 0x40585d
jmp 0x405858
movq %rax, %rbx
jmp 0x405890
movq %rax, %rbx
jmp 0x405884
jmp 0x405869
movq %rax, %rbx
jmp 0x405878
movq %rax, %rbx
leaq -0x100(%rbp), %rdi
callq 0x404baa
leaq -0x90(%rbp), %rdi
callq 0x404baa
leaq -0xb0(%rbp), %rdi
callq 0x409d6a
leaq -0x260(%rbp), %rdi
callq 0x409d6a
leaq -0xc8(%rbp), %rdi
callq 0x409d6a
movq %rbx, %rdi
callq 0x4ba600