---
# System prepended metadata

title: ITSEC ASIA QUALIFICATION 2025 WRITEUP

---

<style>
img[alt=chall-sc] {
  display: block;
  margin: 0 auto;
  width: 100em; 
}

p {
  text-align: justify;
}

p::first-line {
  text-indent: 0;
}
</style>


# ITSEC ASIA QUALIFICATION 2025 WRITEUP
![chall-sc](https://hackmd.io/_uploads/HyZYQE1Ogl.png)
Last week, I had the opportunity to participate in the ITSEC Asia 2025 Qualification Round, which was held from Friday, August 1st to Sunday, August 3rd. The competition was organized by PT ITSEC Asia Tbk and brought together skilled cybersecurity enthusiasts from across the region. I joined the event as part of the team SNI - FLAREITO, and throughout the competition, we faced a series of challenging problems that tested both our technical expertise and teamwork. We managed to secure 4th place on the scoreboard before it was frozen, and even after the freeze, we successfully solved an additional challenge, demonstrating our resilience and focus under pressure.

The competition featured a wide range of categories, including Cryptography, Digital Forensic, Cloud, Blockchain, Reverse Engineering, Web Exploitation, and Binary Exploitation. In this writeup, I have documented several of the medium and hard-level challenges that I personally solved during the event. Each solution offering insight into how I approached and overcame the problems during the qualification round. Hope you enjoy reading it!
# Ingfokan Login
![chall-sc](https://hackmd.io/_uploads/S1PoPGyuge.png)
Given `chall.py` and `secret.py`. The flag variable is defined in the `secret.py`
## chall.py
```python
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
import hashlib
from secret import flag

class Server:
    def __init__(self):
        random = get_random_bytes(256)
        self.password = hashlib.md5(random).hexdigest()
        assert len(flag) == 16
        self.secret = (flag + get_random_bytes(48)).hex()

    def receive_challenge(self, challenge):
        self.clientChallenge = challenge

    def sendChallenge(self, receiver):
        self.challenge = get_random_bytes(16).hex() + get_random_bytes(48).hex()
        print("sending server challenge: ", self.challenge)
        receiver.receive_challenge(self, self.challenge)

    def receive_credential(self, credential):
        self.clientCredential = credential.decode()

    def calculateSessionKey(self):
        salt = b""
        iterations = 100_000
        key_length = 32
        self.calculatedKey = hashlib.pbkdf2_hmac('sha256', server.clientChallenge.encode() + self.challenge.encode() + self.password.encode(), salt, iterations, dklen=key_length)

    def computeCred(self):
        challenge = bytes.fromhex(self.clientChallenge)
        iv = challenge[:16]
        plaintext = challenge[16:]

        cipher_ecb = AES.new(self.calculatedKey, AES.MODE_ECB)
        ciphertext = bytearray()
        shift_reg = bytearray(iv)

        for byte in plaintext:
            encrypted = cipher_ecb.encrypt(bytes(shift_reg))
            keystream_byte = encrypted[0]
            cipher_byte = byte ^ keystream_byte
            ciphertext.append(cipher_byte)

            shift_reg = shift_reg[1:] + bytes([cipher_byte])
            
        result = bytes(ciphertext)
        self.nonce = iv
        self.credential = result.hex()

    def sendCredential(self, receiver, message):
        challenge = bytes.fromhex(message)
        plaintext = challenge
        cipher_ecb = AES.new(self.calculatedKey, AES.MODE_ECB)
        ciphertext = bytearray()
        shift_reg = bytearray(self.nonce)

        for i in range(0, len(plaintext), 16):
            block = plaintext[i:i+16]
            encrypted = cipher_ecb.encrypt(bytes(shift_reg))

            cipher_block = bytes([b ^ e for b, e in zip(block, encrypted)])
            ciphertext.extend(cipher_block)

            shift_reg = bytearray(cipher_block)

        result = bytes(ciphertext).hex()
        print("sending server credential: ", result)
        receiver.receive_credential(self, result)

    def verifyCred(self):
        if self.clientCredential == self.credential:
            print("[+] Authentication Successful.")
            return True
        else:
            print("[!] Authentication Failure!")
            return False

class Client:
    def __init__(self, username, password):
        self.username = hashlib.md5(username.encode()).hexdigest()
        self.password = hashlib.md5(password.encode()).hexdigest()
        self.randombytes = get_random_bytes(48).hex()
        self.serverChallenge = None
        self.serverCredential = None

    def sendChallenge(self, receiver):
        self.challenge = self.username + self.randombytes
        print("sending client challenge: ", self.challenge)
        receiver.receive_challenge(self, self.challenge)

    def receive_challenge(self, serverChallenge):
        self.serverChallenge = serverChallenge

    def sendCredential(self, receiver):
        print("sending client credential: ", self.credential)
        receiver.receive_credential(self, self.credential)

    def receive_credential(self, credential):
        self.serverCredential = credential.decode()

    def calculateSessionKey(self):
        salt = b""
        iterations = 100_000
        key_length = 32
        self.calculatedKey = hashlib.pbkdf2_hmac('sha256', self.challenge.encode() + self.serverChallenge.encode() + self.password.encode(), salt, iterations, dklen=key_length)

    def computeCred(self):
        challenge = bytes.fromhex(self.challenge)
        iv = challenge[:16]
        plaintext = challenge[16:]

        cipher_ecb = AES.new(self.calculatedKey, AES.MODE_ECB)
        ciphertext = bytearray()
        shift_reg = bytearray(iv)

        for byte in plaintext:
            encrypted = cipher_ecb.encrypt(bytes(shift_reg))
            keystream_byte = encrypted[0]
            cipher_byte = byte ^ keystream_byte
            ciphertext.append(cipher_byte)

            shift_reg = shift_reg[1:] + bytes([cipher_byte])

        result = bytes(ciphertext)
        self.credential = result.hex()

class Attacker:
    def __init__(self, client, server):
        self.client = client
        self.server = server

    def relay_challenge(self, receiver, challenge):
        receiver.receive_challenge(challenge)

    def receive_challenge(self, sender, challenge):
        tamp = input(f"(tamper): ")
        if tamp == "fwd":
            msg_sent = challenge
        else:
            msg_sent = tamp

        if sender == self.server:
            self.relay_challenge(self.client, msg_sent)
        elif sender == self.client:
            self.relay_challenge(self.server, msg_sent)

    def relay_credential(self, receiver, challenge):
        receiver.receive_credential(challenge)

    def receive_credential(self, sender, challenge):
        tamp = input(f"(tamper): ")
        if tamp == "fwd":
            msg_sent = challenge.encode()
        else:
            msg_sent = tamp.encode()

        if sender == self.server:
            self.relay_credential(self.client, msg_sent)
        elif sender == self.client:
            self.relay_credential(self.server, msg_sent)

if __name__ == "__main__":
    print("===HAPPY HAPPY ITSEC LOGoN PAGE==")
    username = input("Username: ")
    password = input("Password: ")

    client = Client(username, password)
    server = Server()
    attacker = Attacker(client, server)

    def begin_communication():
        while True:
            client.sendChallenge(attacker)
            server.sendChallenge(attacker)
            client.calculateSessionKey()
            server.calculateSessionKey()
            client.computeCred()
            server.computeCred()
            client.sendCredential(attacker)
            result = server.verifyCred()
            if result:
                server.sendCredential(attacker, server.clientChallenge)
                server.sendCredential(attacker, server.secret)


    begin_communication()
```
In this challenge, the server and client perform a custom CFB-style handshake using AES-ECB and a PBKDF2-derived session key, but the protocol is fatally broken. It is broken in two ways. By letting us submit a 16-byte (32-hex) client challenge, we force both client and server to compute an empty credential (since there’s no plaintext to encrypt), so Server.verifyCred() blindly succeeds. 

After authentication, the server reuses the same AES-ECB keystream (starting with K0 = AES_ECB(key, IV)) to encrypt both our IV (which we chose and thus know) and its secret (whose first 16 bytes are the flag). By capturing the ciphertext of our IV (C0 = IV ⊕ K0) and the ciphertext of the secret’s first block (Cflag = flag ⊕ K0), we recover K0 = IV ⊕ C0 and immediately decrypt flag = Cflag ⊕ K0.

## Solver
```python
#!/usr/bin/env python3
from pwn import remote, context, log
from Crypto.Util.strxor import strxor


host, port = "nc 54.254.152.24 2025".split(" ")[1:3]
# io = process(["python3", "chall.py"])
io = remote(host, port)
context.log_level = 'debug'
IV     = b"\x00" * 16
IV_HEX = IV.hex()
io.recvuntil(b"Username:")
io.sendline(b"a")
io.recvuntil(b"Password:")
io.sendline(b"b")

C0     = None
Cflag  = None
last_line = ""

while True:
    data = io.recvuntil(b"(tamper): ")
    lines = data.split(b"\n")
   
    for raw in lines[:-1]:
        line = raw.strip().decode()
        last_line = line

        if line.startswith("sending server credential:"):
            hexstr = line.split(":", 1)[1].strip()
            if C0 is None:
                C0 = bytes.fromhex(hexstr)
            else:
                Cflag = bytes.fromhex(hexstr[:32])
                break
    if Cflag:
        break

    if last_line.startswith("sending client challenge:"):
        io.sendline(IV_HEX.encode())    
    elif last_line.startswith("sending client credential:"):
        io.sendline(b"")              
    else:
        io.sendline(b"fwd")            

K0   = strxor(IV, C0)
flag = strxor(Cflag, K0)
print(flag)
```
![chall-sc](https://hackmd.io/_uploads/BkjsFzJdel.png)

# Simple RSA
![chall-sc](https://hackmd.io/_uploads/SylyOfkuex.png)
We are given simple-rsa.txt. It is a text file that contains RSA parameters such as n (the RSA modulus), e (public key), and c (ciphertext).
```python
n=46014922953495823590792625328453518537759942907385288519972078748310115766076552700510034869862113134248890854832840744264858628129833098791884587479017453857115837697620445597251303101376348636616052018461298256839495151809137245487519880704838153895045646394408937224134545491323473393082791677399084623521903889071358476406581797209920917897120552647085367045771350369928714101952885552482344272084295440750349944373207286646963542000298850932632533690423253410522645569134022639503146287927023894946464828496242988631752199042717365408818100180895221911662249505805008325089437657448443933868958820817910558471293
e=268435459
c=11314339403359567780692601069815710743165402544988203918151340837645606912959402641126954145280660570762982247771917542719878231291766614862358489243957964439916749413680930944615063921439539055825420053337614980961682681555035169099974121913924178155258600619452395067299085627896352720005233379231312709290583412444031184554596453797817161128552414571518324581806767819754389759232708355060229677061961742874649289853359807929735947675898971334344822872967188360102835994032157447342986467879631904720037815396636573047116651469718152143887897849178164454377805656650083129515711040911387971255712009611360895624486
```
![chall-sc](https://hackmd.io/_uploads/SJf6_Gydex.png)
With no source code, this challenge is hard. Since the n is 2049 bits, we can suspect, N = p * q * r where each prime is 683 bits. I tried to factorize the n with Pollard’s p-1 algorithm. The factorization failed, and I found William’s p+1 algorithm (https://github.com/Giapppp/RSA-Attacks/blob/main/William's%20p%2B1%20Method/attack.py) to retrieve p. Using it as a reference and modify it a little bit, it helped me to retrieve the value of p. 
## Williams p+1?

This is how it works:

---

### Lucas Sequence Exponentiation

We work in $\mathbb{Z}/N\mathbb{Z}$ with the Lucas sequence
$$
V_0 = 2,\quad
V_1 = P = v,\quad
V_k = P\,V_{k-1} - V_{k-2}
$$

```python
def mlucas(v, a, n):
    v1, v2 = v % n, (v*v - 2) % n
    for bit in bin(a)[3:]:
        if bit == '0':
            # Lucas doubling
            v1, v2 = (v1*v1 - 2) % n, (v1*v2 - v) % n
        else:
            # Lucas addition
            v1, v2 = (v1*v2 - v) % n, (v2*v2 - 2) % n
    return v1
```
### Stage 1 (Bounded Exponentiation)

1. Fix a smoothness bound \(B_1\).  
2. Compute the exponent  
   $$
   M \;=\;\prod_{q \le B_1} q^{\lfloor \log_q(p+1)\rfloor}
   $$
3. Compute  
   $$
   V = V_M \bmod N,
   \qquad
   g = \gcd\bigl(V - 2,\;N\bigr)
   $$
4. If \(1 < g < N\), then \(g = p\).

```python
from sympy import primerange
from math import log
from Crypto.Util.number import GCD

def williams_stage1(n, B1, v=2):
    V = v % n
    for q in primerange(2, B1+1):
        exp = int(log(p+1, q))
        if exp > 0:
            V = mlucas(V, pow(q, exp), n)
    g = GCD(V - 2, n)
    return int(g) if 1 < g < n else None
```
### Stage 2 (Optional Large-Prime Search)
If p+1 has a large prime factor r with 
$$
B_{1} < r \le B_{2}
$$
then for each such r:
1. Compute
   $$
   U = V_{M\cdot r} \bmod N
   $$
2. Check
   $$
   g = \gcd\bigl(U - 2,\;N\bigr)
   $$
3. Stop when \(1 < g < N\).

```python
def williams_stage2(n, B1, B2, V_B1):
    for r in primerange(B1+1, B2+1):
        U = mlucas(V_B1, r, n)
        g = GCD(U - 2, n)
        if 1 < g < n:
            return int(g)
    return None
```
### Combining It

1. Choose smoothness bounds \(B_1\) and \(B_2\).  
2. Try several Lucas seeds:

$$
v \;\in\; \{2,\,3,\,5,\,\dots\}
$$

   - **Stage 1:** call `williams_stage1(n, B1, v)`; if it returns a nontrivial factor \(p\), you’re done.  
   - Otherwise, reconstruct the partial-decryption value for Stage 2 by  
     1. Setting  
        $$
        V \;=\; v \bmod N
        $$
     2. Computing  
        $$
        V = V_M \bmod N
        $$
       exactly as in Stage 1 to obtain `V_B1`.  
   - **Stage 2:** call `williams_stage2(n, B1, B2, V_B1)`  
3. If none of the seeds yield a factor, increase \(B_1\) or \(B_2\), or try new seeds \(v\).

```python
import random
from sympy import primerange
from math import log
from Crypto.Util.number import GCD

def williams_p_plus_one(n, B1=1<<20, B2=None):
    if B2 is None:
        B2 = B1 * 50
    for v in [2, 3, 5, random.randrange(6, 1<<16)]:
        # Stage 1
        p = williams_stage1(n, B1, v)
        if p:
            return p
        # Recompute V_M for Stage 2
        V = v % n
        for q in primerange(2, B1+1):
            exp = int(log(p+1, q))
            if exp > 0:
                V = mlucas(V, pow(q, exp), n)
        # Stage 2
        p2 = williams_stage2(n, B1, B2, V)
        if p2:
            return p2
    return None
```

Since I already have p, I calculate q and r using fermat factorization. 
## Fermat’s Factorization (for the cofactor)

Once we’ve extracted \(p\), set the cofactor:
$$
C = \frac{N}{p}
$$

Fermat’s method finds integers \(a,b\) such that
$$
a^2 - b^2 = C
\quad\Longrightarrow\quad
C = (a - b)\,(a + b)
$$

1. **Initialize**  
   $$
   a = \lceil \sqrt{C}\rceil
   $$

2. Repeat until success:

   $$
   b^2 = a^2 - C
   $$

   - If \(b^2\) is a perfect square, break.  
   - Otherwise:  
     $$
     a  = a + 1
     $$
     and retry.

3. **Compute** the factors
   $$
   b = \sqrt{a^2 - C},\quad
   q = a - b,\quad
   r = a + b.
   $$

```python
from math import isqrt

def fermat_factor(C):
    a = isqrt(C)
    if a*a < C:
        a += 1
    while True:
        b2 = a*a - C
        b = isqrt(b2)
        if b*b == b2:
            return a - b, a + b
        a += 1
```
## Shumow Attack
After getting the three factors of N, I bruteforced the known prefix `ITSEC{` with `Shumow Attack` (https://eprint.iacr.org/2020/1059). The interesting parts on this paper are section 3.1 and section 3.2. Based on section 3.1, the public exponent e divides the group order, exponentiation ceases to be injective and instead factors through an e-torsion subgroup 𝐸 of size e. Concretely, we first compute the reduced totient
![chall-sc](https://hackmd.io/_uploads/ryG7FG1_ex.png)
Since we have three factors of N, the reduced totient is ((p-1)(q-1)(r-1))/e. Then find the unique multiplicative inverse d and perform partial decryption to find a
![chall-sc](https://hackmd.io/_uploads/ryeEYM1uxg.png)
Next we search for a generator g_E of 𝐸 by testing small candidates g until:
![chall-sc](https://hackmd.io/_uploads/rJErFzkuxe.png)
The last step is that we need to recover the lost factor of the plaintext by enumerating i knowing that one of these e values must be the original message
![chall-sc](https://hackmd.io/_uploads/rkb8FM1_lx.png)
## Solver
```python
from math import isqrt, log
from Crypto.Util.number import GCD, inverse
from sympy import primerange
import random
from tqdm import tqdm
import time


n = 46014922953495823590792625328453518537759942907385288519972078748310115766076552700510034869862113134248890854832840744264858628129833098791884587479017453857115837697620445597251303101376348636616052018461298256839495151809137245487519880704838153895045646394408937224134545491323473393082791677399084623521903889071358476406581797209920917897120552647085367045771350369928714101952885552482344272084295440750349944373207286646963542000298850932632533690423253410522645569134022639503146287927023894946464828496242988631752199042717365408818100180895221911662249505805008325089437657448443933868958820817910558471293
e = 268435459
c = 11314339403359567780692601069815710743165402544988203918151340837645606912959402641126954145280660570762982247771917542719878231291766614862358489243957964439916749413680930944615063921439539055825420053337614980961682681555035169099974121913924178155258600619452395067299085627896352720005233379231312709290583412444031184554596453797817161128552414571518324581806767819754389759232708355060229677061961742874649289853359807929735947675898971334344822872967188360102835994032157447342986467879631904720037815396636573047116651469718152143887897849178164454377805656650083129515711040911387971255712009611360895624486


def mlucas(v, a, n):
    v1, v2 = v % n, (v*v - 2) % n
    for bit in bin(a)[3:]:
        if bit == '0':
            v1, v2 = (v1*v1 - 2) % n, (v1*v2 - v) % n
        else:
            v1, v2 = (v1*v2 - v) % n, (v2*v2 - 2) % n
    return v1


def williams_stage1(n, B1, v=2):
    V = v % n
    for p in tqdm(primerange(2, B1+1),
                  desc=f"williams p+1 stage 1 (B1={B1})",
                  unit=" prime"):
        exp = int(log(B1, p))
        if exp > 0:
            V = mlucas(V, pow(p, exp), n)
    g = GCD(V-2, n)
    return int(g) if 1 < g < n else None


def williams_stage2(n, B1, B2, V_B1):
    for r in tqdm(primerange(B1+1, B2+1),
                  desc=f"williams p+1 stage 2 (B2={B2})",
                  unit=" prime"):
        U = mlucas(V_B1, r, n)
        g = GCD(U-2, n)
        if 1 < g < n:
            return int(g)
    return None


def williams_p_plus_one(n, B1=1<<20, B2=None):
    if B2 is None:
        B2 = B1 * 50
    for v in [2, 3, 5, random.randint(6, 1<<16)]:
        g = williams_stage1(n, B1, v)
        if g:
            return g
        V = v % n
        for p in primerange(2, B1+1):
            exp = int(log(B1, p))
            if exp > 0:
                V = mlucas(V, pow(p, exp), n)
        g2 = williams_stage2(n, B1, B2, V)
        if g2:
            return g2
    return None


# factorize pake williams p+1 + fermat
p = williams_p_plus_one(n, B1=1<<18, B2=1<<23)
print("p =", p)


def is_square(x):
    y = isqrt(x)
    return y if y*y == x else None


qr = n // p


def fermat_factor(N):
    a = isqrt(N)
    b = a*a - N
    with tqdm(desc="fermat factorization", unit=" iter") as bar:
        while b < 0 or not is_square(b):
            a += 1
            b = a*a - N
            bar.update(1)
    p_ = a - isqrt(b)
    q_ = N // p_
    return (p_, q_)


q, r = fermat_factor(qr)
print("q =", q)
print("r =", r)


# shumow attack
N = p * q * r
assert N == n
totient = (p - 1) * (q - 1) * (r - 1)


reduced_totient = totient // e
for g in range(2, 1 << 20):
    gE = pow(g, reduced_totient, N)
    if gE != 1 and pow(gE, e, N) == 1:
        break


d  = inverse(e, reduced_totient)
a = pow(c, d, N)
i      = 242_737_470 # initialnya 1. sekitar 1,5 jam jalanin dapet nilai i yang berhasil dapetin prefixnya wkwkwk. jadi langsung aja set ke nilai i yang dah berhasil dapet prefixnya biar ga nunggu
flag = int((a * pow(gE, i, N)) % N).to_bytes((int((a * pow(gE, i, N)) % N).bit_length() + 7) // 8, "big")
print(flag)
```
![chall-sc](https://hackmd.io/_uploads/r1nPYfJ_el.png)

# Minesweeper
![chall-sc](https://hackmd.io/_uploads/SkzQ5z1_xx.png)
Given a stripped portable executable (EXE) file which is a minesweeper game
![chall-sc](https://hackmd.io/_uploads/SkhN5GJdxg.png)
Since the program is stripped, we need to analyze and rename the functions, variables, and add some comments to the decompilation result. These are the functions and variables that I have already renamed, and the Code that I have commented. 
## The main game function
```c
// Main minesweeper game function.
//
// Game Object Layout (v19 array):
// - v19[0] = Game vtable pointer
// - v19[2] = graphics context pointer (initialized to 0)
// - v19[26-61] = Sprite objects (4 total, 12 DWORDs each)
// - v19[62-73] = Another sprite object
// - v19[74-76] = Additional graphics data (12 bytes)
// - v19[77-145] = Graphics context data
// - v19[146] = Boolean flag (initialized to 0)
// - v19[147] = Contains win message "You win!!"
//
// Game Flow:
// 1. Initialize game object with vtable
// 2. Initialize graphics context
// 3. Initialize 5 sprite objects for game pieces
// 4. Initialize graphics transform data
// 5. Set up board parameters (8x8, 16 mines, 48 total)
// 6. Main game loop:
//    - Poll for input events (mouse/keyboard)
//    - Handle cell reveals and flag toggles
//    - Update graphics matrices
//    - Render the game board
//    - Check for win/lose conditions
// 7. Clean up resources on exit
//
// Input Handling:
// - Mouse clicks: Convert screen coords to grid (divide by 22)
// - Keyboard: H/G/J keys for navigation, I key for action
// - ESC key (73) exits game
//
// The game uses a sophisticated PRNG-based mine placement where
// mine probability = 48 / total_cells for each cell.
int __cdecl minesweeper_main(int argc, const char **argv, const char **envp)
{
  int v3; // ecx
  int v4; // ecx
  int v6; // [esp+4h] [ebp-2C4h] BYREF
  int v7; // [esp+8h] [ebp-2C0h] BYREF
  int v8; // [esp+Ch] [ebp-2BCh] BYREF
  int v9; // [esp+10h] [ebp-2B8h] BYREF
  _DWORD *v10; // [esp+14h] [ebp-2B4h] BYREF
  _DWORD *v11; // [esp+18h] [ebp-2B0h] BYREF
  _DWORD *v12; // [esp+1Ch] [ebp-2ACh] BYREF
  _DWORD *v13; // [esp+20h] [ebp-2A8h] BYREF
  _DWORD *v14; // [esp+24h] [ebp-2A4h] BYREF
  _DWORD v15[164]; // [esp+28h] [ebp-2A0h] BYREF
  int v16; // [esp+2C4h] [ebp-4h]

  memset(v15, 0, sizeof(v15));
  v15[0] = &Game::`vftable';
  memset(&v15[3], 0, 12);
  initialize_graphics_context((char *)&v15[6]);
  v16 = 0;
  initialize_sprite((int)&v15[26]);
  LOBYTE(v16) = 1;
  initialize_sprite((int)&v15[38]);
  LOBYTE(v16) = 2;
  initialize_sprite((int)&v15[50]);
  LOBYTE(v16) = 3;
  initialize_sprite((int)&v15[62]);
  memset(&v15[74], 0, 12);
  initialize_transform_matrix(&v15[77]);
  LOBYTE(v15[146]) = 0;
  LOBYTE(v16) = 6;
  LOWORD(v15[147]) = 0;
  v15[2] = 0;
  strcpy((char *)&v15[147] + 2, "You win!!");
  initialize_game_window(v15);
  load_game_textures(v15);
  v14 = &v15[62];
  v13 = &v15[74];
  v12 = &v15[50];
  v11 = &v15[38];
  v10 = &v15[26];
  v9 = 16;
  v8 = 48;
  v7 = 8;
  v6 = 8;
  if ( LOBYTE(v15[146]) )
  {
    cleanup_board_data(&v15[123], v3);
    LOBYTE(v15[146]) = 0;
  }
  initialize_game_board(&v15[123], &v6, &v7, &v8, &v9, (int *)&v10, (int *)&v11, (int *)&v12, (int *)&v13, (int *)&v14);
  v16 = 7;
  while ( is_window_open((_DWORD *)v15[2]) )
  {
    handle_input_events((int)v15);
    check_game_state_and_display_result((int)v15);
  }
  v4 = v15[2];
  v15[0] = &Game::`vftable';
  if ( v15[2] )
    (**(void (__thiscall ***)(_DWORD, int))v15[2])(v15[2], 1);
  if ( LOBYTE(v15[146]) )
    cleanup_board_data(&v15[123], v4);
  cleanup_sprite_vector(&v15[74]);
  release_sprite_texture(&v15[62]);
  release_sprite_texture(&v15[50]);
  release_sprite_texture(&v15[38]);
  release_sprite_texture(&v15[26]);
  sub_403660(&v15[6]);
  return 0;
}```
## initialization of transform matrix
```c
_DWORD *__thiscall initialize_transform_matrix(_DWORD *this)
{
  *this = 1140457472;
  this[1] = 1140457472;
  this[2] = 1148846080;
  this[3] = 1148846080;
  this[4] = 0;
  this[5] = 0;
  this[6] = 0;
  this[7] = 1065353216;
  this[8] = 1065353216;
  this[9] = 0;
  this[10] = 0;
  this[11] = 1065353216;
  this[12] = 1065353216;
  sub_402940(this + 13);
  sub_402940(this + 29);
  *((_WORD *)this + 90) = 0;
  return this;
}
```
## initialization of game window
```c
// Initializes the main game window and graphics context.
//
// Creates a 300x500 pixel window titled "MineSweeper" using SFML graphics library.
// Sets up the rendering context, view matrices, and prepares the graphics
// system for sprite rendering.
//
// The function:
// 1. Sets window dimensions (300x500)
// 2. Allocates graphics context memory (600 bytes)
// 3. Creates window with title "MineSweeper"
// 4. Initializes view/projection matrices
// 5. Sets up coordinate system for 22x22 pixel grid cells
// 6. Updates view matrix in game object (+77 offset)
//
// The window serves as the main rendering target for the sprite-based
// Minesweeper board display.
_BYTE *__thiscall initialize_game_window(_DWORD *this)
{
  __int64 v2; // xmm0_8
  int v3; // eax
  unsigned int v4; // ecx
  void *v5; // edx
  void (__thiscall ***v6)(_DWORD, int); // eax
  float v7; // xmm2_4
  float v8; // xmm1_4
  int v10[6]; // [esp+Ch] [ebp-5Ch] BYREF
  char v11; // [esp+24h] [ebp-44h]
  void *Block[5]; // [esp+28h] [ebp-40h] BYREF
  int v13; // [esp+3Ch] [ebp-2Ch]
  int v14; // [esp+40h] [ebp-28h] BYREF
  struct std::locale::_Locimp *v15; // [esp+44h] [ebp-24h]
  void *v16; // [esp+48h] [ebp-20h]
  int v17; // [esp+4Ch] [ebp-1Ch]
  unsigned int v18; // [esp+50h] [ebp-18h] BYREF
  unsigned int v19; // [esp+54h] [ebp-14h]
  int v20; // [esp+64h] [ebp-4h]

  this[3] = 300;
  this[4] = 500;
  v16 = operator new(0x258u);
  v20 = 2;
  memset(v16, 0, 0x258u);
  memset(v10, 0, 12);
  v10[3] = 1;
  v10[4] = 1;
  v10[5] = 0;
  v11 = 0;
  v15 = std::locale::_Init(1);
  v17 = 1;
  sub_438B30(Block, (int)"MineSweeper", (int)&v14);
  v20 = 3;
  v2 = *(_QWORD *)(this + 3);
  v17 = 3;
  v3 = sub_40CBD0(v2, this[5], (int)Block, 7, 0, (int)v10);
  v20 = 4;
  v4 = v13;
  this[2] = v3;
  if ( v4 > 3 )
  {
    v5 = Block[0];
    if ( 4 * v4 + 4 >= 0x1000 )
    {
      v5 = (void *)*((_DWORD *)Block[0] - 1);
      if ( (unsigned int)((char *)Block[0] - (char *)v5 - 4) > 0x1F )
        invalid_parameter_noinfo_noreturn();
    }
    sub_49F7E3(v5);
  }
  v20 = -1;
  Block[4] = 0;
  v13 = 3;
  Block[0] = 0;
  if ( v15 )
  {
    v6 = (void (__thiscall ***)(_DWORD, int))(*(int (__thiscall **)(struct std::locale::_Locimp *))(*(_DWORD *)v15 + 8))(v15);
    if ( v6 )
      (**v6)(v6, 1);
  }
  (*(void (__thiscall **)(int, unsigned int *))(*(_DWORD *)(this[2] + 56) + 4))(this[2] + 56, &v18);
  v7 = (float)v19;
  sub_408F40((float)v18 * 0.5, v7 * 0.5);
  v8 = (float)v19;
  ((void (__stdcall *)(_DWORD, _DWORD))sub_408F60)((float)v18, LODWORD(v8));
  return update_view_matrix((_BYTE *)(this[2] + 56), this + 77);
}
```
## initialization of graphic context
```c
_DWORD *__thiscall sub_403460(char *this)
{
  _DWORD *v1; // esi
  _DWORD *v2; // edi
  _DWORD *v3; // eax
  _DWORD *v4; // esi
  _DWORD *v5; // eax
  int v6; // ecx
  _DWORD *v7; // eax
  _DWORD *result; // eax
  unsigned int v9; // ecx
  _DWORD *v10; // [esp+14h] [ebp-14h]

  v1 = this;
  *(_DWORD *)this = 0;
  *((_DWORD *)this + 1) = 0;
  this[8] = 1;
  *(_OWORD *)(this + 12) = 0LL;
  *((_DWORD *)this + 7) = 0;
  *((_DWORD *)this + 8) = 15;
  this[12] = 0;
  v2 = this + 36;
  *((_DWORD *)this + 9) = 0;
  *((_DWORD *)this + 10) = 0;
  *((_DWORD *)this + 11) = 0;
  v3 = operator new(0x70u);
  *v3 = v3;
  v3[1] = v3;
  v2[1] = v3;
  v2[3] = 0;
  v2[4] = 0;
  v2[5] = 0;
  v2[6] = 7;
  v2[7] = 8;
  *v2 = 1065353216;
  v10 = v3;
  if ( (unsigned int)((int)v2[4] >> 2) >= 0x10 )
  {
    v9 = (unsigned int)(v2[4] + 3) >> 2;
    if ( v9 )
      memset32(0, (int)v3, v9);
  }
  else
  {
    v4 = operator new(0x40u);
    v5 = (_DWORD *)v2[3];
    v6 = (v2[5] - (int)v5) >> 2;
    if ( v6 )
    {
      if ( (unsigned int)(4 * v6) >= 0x1000 )
      {
        if ( (unsigned int)v5 - *(v5 - 1) - 4 > 0x1F )
          invalid_parameter_noinfo_noreturn();
        v5 = (_DWORD *)*(v5 - 1);
      }
      sub_49F7E3(v5);
    }
    v7 = v4 + 16;
    v2[3] = v4;
    v2[4] = v4 + 16;
    v2[5] = v4 + 16;
    do
      *v4++ = v10;
    while ( v4 != v7 );
    v1 = this;
  }
  v1[17] = 0;
  result = v1;
  v1[18] = 0;
  v1[19] = 0;
  return result;
}
```
## load game texture
```c
// Loads all game textures and initializes sprite objects.
//
// Textures loaded:
// 1. consola.ttf - Game font
// 2. covered.png - Covered/unrevealed cells  
// 3. empty.png - Empty revealed cells
// 4. flag.png - Flagged cells
// 5. mine.png - Mine cells
// 6. field_1.png through field_8.png - Number textures (1-8 adjacent mines)
//
// The function maintains a vector of sprite objects (48 bytes each) and creates
// exactly 10 sprites total for all the different cell states and numbers.
//
// Each sprite gets a unique ID and texture assigned. The sprites are stored
// in the game object's sprite vector at offset +74.
//
// If any texture fails to load, throws a runtime_error exception.
int __thiscall load_game_textures(_DWORD *this)
{
  char v2; // al
  void *v3; // edx
  char v4; // al
  void *v5; // edx
  char v6; // al
  void *v7; // edx
  char v8; // al
  void *v9; // edx
  char v10; // al
  void *v11; // edx
  unsigned int v12; // ecx
  int v13; // esi
  _DWORD *v14; // eax
  int v15; // edi
  unsigned int v16; // eax
  int v17; // eax
  _DWORD *v18; // edi
  _DWORD *v19; // ecx
  int v20; // edi
  int v21; // esi
  __int64 *v22; // ecx
  unsigned int v23; // edi
  int v24; // edx
  __int64 *v25; // eax
  char v26; // al
  void *v27; // edx
  void *v28; // edx
  void *v29; // edx
  void *v30; // edx
  int result; // eax
  int v32; // eax
  int v33; // eax
  int v34; // eax
  int v35; // [esp-8h] [ebp-100h]
  unsigned int v36; // [esp-4h] [ebp-FCh]
  _BYTE v37[24]; // [esp+Ch] [ebp-ECh] BYREF
  _BYTE v38[24]; // [esp+3Ch] [ebp-BCh] BYREF
  void *v39[5]; // [esp+54h] [ebp-A4h] BYREF
  unsigned int v40; // [esp+68h] [ebp-90h]
  _BYTE pExceptionObject[12]; // [esp+6Ch] [ebp-8Ch] BYREF
  void *Src[5]; // [esp+78h] [ebp-80h] BYREF
  unsigned int v43; // [esp+8Ch] [ebp-6Ch]
  void *v44[4]; // [esp+90h] [ebp-68h] BYREF
  __int64 v45; // [esp+A0h] [ebp-58h]
  int v46; // [esp+A8h] [ebp-50h]
  int v47; // [esp+ACh] [ebp-4Ch]
  int v48; // [esp+B0h] [ebp-48h]
  void *Block[5]; // [esp+B4h] [ebp-44h] BYREF
  unsigned int v50; // [esp+C8h] [ebp-30h]
  int v51; // [esp+CCh] [ebp-2Ch]
  int v52; // [esp+D0h] [ebp-28h]
  char v53; // [esp+D7h] [ebp-21h]
  int v54[4]; // [esp+D8h] [ebp-20h] BYREF
  int v55; // [esp+F4h] [ebp-4h]

  LOBYTE(v52) = 0;
  sub_405880(Block, (int)"contents/consola.ttf");
  v55 = 0;
  v2 = sub_40C670((LPCWCH)Block);
  v55 = -1;
  v53 = v2;
  if ( v50 > 7 )
  {
    v3 = Block[0];
    if ( 2 * v50 + 2 >= 0x1000 )
    {
      v3 = (void *)*((_DWORD *)Block[0] - 1);
      if ( (unsigned int)((char *)Block[0] - (char *)v3 - 4) > 0x1F )
LABEL_4:
        invalid_parameter_noinfo_noreturn();
    }
    sub_49F7E3(v3);
    v2 = v53;
  }
  if ( !v2 )
  {
    sub_402A30("Failed to load consola.ttf");
LABEL_75:
    CxxThrowException(&v54[1], (_ThrowInfo *)&_TI2_AVruntime_error_std__);
  }
  LOBYTE(v52) = 0;
  memset(v54, 0, sizeof(v54));
  sub_405880(Block, (int)"contents/covered.png");
  v55 = 1;
  v4 = sub_409790((LPCWCH)Block, 0, (int)v54);
  v55 = -1;
  v53 = v4;
  if ( v50 > 7 )
  {
    v5 = Block[0];
    if ( 2 * v50 + 2 >= 0x1000 )
    {
      v5 = (void *)*((_DWORD *)Block[0] - 1);
      if ( (unsigned int)((char *)Block[0] - (char *)v5 - 4) > 0x1F )
        invalid_parameter_noinfo_noreturn();
    }
    sub_49F7E3(v5);
    v4 = v53;
  }
  if ( !v4 )
  {
    sub_402A30("Failed to load covered.png");
    goto LABEL_75;
  }
  LOBYTE(v52) = 0;
  memset(v54, 0, sizeof(v54));
  sub_4058C0(Block);
  v55 = 2;
  v6 = sub_409790((LPCWCH)Block, 0, (int)v54);
  v55 = -1;
  v53 = v6;
  if ( v50 > 7 )
  {
    v7 = Block[0];
    if ( 2 * v50 + 2 >= 0x1000 )
    {
      v7 = (void *)*((_DWORD *)Block[0] - 1);
      if ( (unsigned int)((char *)Block[0] - (char *)v7 - 4) > 0x1F )
        invalid_parameter_noinfo_noreturn();
    }
    sub_49F7E3(v7);
    v6 = v53;
  }
  if ( !v6 )
  {
    sub_402A30("Failed to load empty.png");
    goto LABEL_75;
  }
  LOBYTE(v52) = 0;
  memset(v54, 0, sizeof(v54));
  sub_405880(Block, (int)"contents/flag.png");
  v55 = 3;
  v8 = sub_409790((LPCWCH)Block, 0, (int)v54);
  v55 = -1;
  v53 = v8;
  if ( v50 > 7 )
  {
    v9 = Block[0];
    if ( 2 * v50 + 2 >= 0x1000 )
    {
      v9 = (void *)*((_DWORD *)Block[0] - 1);
      if ( (unsigned int)((char *)Block[0] - (char *)v9 - 4) > 0x1F )
        invalid_parameter_noinfo_noreturn();
    }
    sub_49F7E3(v9);
    v8 = v53;
  }
  if ( !v8 )
  {
    sub_402A30("Failed to load flag.png");
    goto LABEL_75;
  }
  LOBYTE(v52) = 0;
  memset(v54, 0, sizeof(v54));
  sub_405880(Block, (int)"contents/mine.png");
  v55 = 4;
  v10 = sub_409790((LPCWCH)Block, 0, (int)v54);
  v55 = -1;
  v53 = v10;
  if ( v50 > 7 )
  {
    v11 = Block[0];
    v12 = 2 * v50 + 2;
    if ( v12 >= 0x1000 )
    {
      v11 = (void *)*((_DWORD *)Block[0] - 1);
      v12 = 2 * v50 + 37;
      if ( (unsigned int)((char *)Block[0] - (char *)v11 - 4) > 0x1F )
        invalid_parameter_noinfo_noreturn();
    }
    v36 = v12;
    sub_49F7E3(v11);
    v10 = v53;
  }
  if ( !v10 )
  {
    sub_402A30("Failed to load mine.png");
    goto LABEL_75;
  }
  v13 = this[75];
  v14 = this + 74;
  v15 = this[74];
  v51 = (int)v14;
  v16 = (v13 - v15) / 48;
  v52 = v16;
  if ( v16 <= 0xA )
  {
    v19 = (_DWORD *)v51;
    if ( v16 < 0xA )
    {
      v19 = (_DWORD *)v51;
      if ( (unsigned int)((*(_DWORD *)(v51 + 8) - v15) / 48) >= 0xA )
      {
        v20 = 10 - v52;
        v54[1] = v13;
        v54[2] = v13;
        v54[3] = v51;
        v55 = 5;
        if ( v52 != 10 )
        {
          do
          {
            initialize_sprite(v13);
            v13 += 48;
            v54[2] = v13;
            --v20;
          }
          while ( v20 );
          v19 = (_DWORD *)v51;
        }
        v55 = -1;
        v19[1] = v13;
      }
      else
      {
        sub_405930(v35, v36);
        v19 = (_DWORD *)v51;
      }
    }
  }
  else
  {
    v17 = v15 + 480;
    v52 = v17;
    v18 = (_DWORD *)(v15 + 480);
    if ( v17 != v13 )
    {
      do
      {
        release_sprite_texture(v18);
        v18 += 12;
      }
      while ( v18 != (_DWORD *)v13 );
      v17 = v52;
    }
    v19 = (_DWORD *)v51;
    *(_DWORD *)(v51 + 4) = v17;
  }
  LOBYTE(v46) = 0;
  v52 = 1;
  v21 = 48;
  while ( 1 )
  {
    v48 = v21 + *v19;
    memset(v54, 0, sizeof(v54));
    sub_404E30(v39);
    v55 = 6;
    v22 = (__int64 *)sub_404FC0(v39);
    LOBYTE(v55) = 7;
    v23 = *((_DWORD *)v22 + 5);
    v24 = *((_DWORD *)v22 + 4);
    if ( v23 - v24 < 4 )
    {
      LOBYTE(v47) = 0;
      v22 = (__int64 *)sub_405530(v22, 4, v47, (int)".png", 4u);
    }
    else
    {
      *((_DWORD *)v22 + 4) = v24 + 4;
      v25 = v22;
      if ( v23 > 0xF )
        v25 = *(__int64 **)v22;
      strcpy((char *)v25 + v24, ".png");
    }
    v45 = 0LL;
    *(_OWORD *)v44 = 0LL;
    *(_OWORD *)v44 = *(_OWORD *)v22;
    v45 = v22[2];
    *((_DWORD *)v22 + 4) = 0;
    *((_DWORD *)v22 + 5) = 15;
    *(_BYTE *)v22 = 0;
    LOBYTE(v55) = 8;
    sub_4058F0(Src, (int)v44);
    LOBYTE(v55) = 9;
    v26 = sub_409790((LPCWCH)Src, 0, (int)v54);
    LOBYTE(v55) = 8;
    v53 = v26;
    if ( v43 > 7 )
    {
      v27 = Src[0];
      if ( 2 * v43 + 2 >= 0x1000 )
      {
        v27 = (void *)*((_DWORD *)Src[0] - 1);
        if ( (unsigned int)((char *)Src[0] - (char *)v27 - 4) > 0x1F )
          goto LABEL_4;
      }
      sub_49F7E3(v27);
    }
    LOBYTE(v55) = 7;
    Src[4] = 0;
    v43 = 7;
    LOWORD(Src[0]) = 0;
    if ( HIDWORD(v45) > 0xF )
    {
      v28 = v44[0];
      if ( (unsigned int)(HIDWORD(v45) + 1) >= 0x1000 )
      {
        v28 = (void *)*((_DWORD *)v44[0] - 1);
        if ( (unsigned int)((char *)v44[0] - (char *)v28 - 4) > 0x1F )
          goto LABEL_4;
      }
      sub_49F7E3(v28);
    }
    LOBYTE(v55) = 6;
    v45 = 0xF00000000LL;
    LOBYTE(v44[0]) = 0;
    if ( v50 > 0xF )
    {
      v29 = Block[0];
      if ( v50 + 1 >= 0x1000 )
      {
        v29 = (void *)*((_DWORD *)Block[0] - 1);
        if ( (unsigned int)((char *)Block[0] - (char *)v29 - 4) > 0x1F )
          goto LABEL_4;
      }
      sub_49F7E3(v29);
    }
    v55 = -1;
    Block[4] = 0;
    v50 = 15;
    LOBYTE(Block[0]) = 0;
    if ( v40 > 0xF )
    {
      v30 = v39[0];
      if ( v40 + 1 >= 0x1000 )
      {
        v30 = (void *)*((_DWORD *)v39[0] - 1);
        if ( (unsigned int)((char *)v39[0] - (char *)v30 - 4) > 0x1F )
          goto LABEL_4;
      }
      sub_49F7E3(v30);
    }
    if ( !v53 )
    {
      v32 = sub_403180(v38, v52);
      v55 = 10;
      v33 = sub_404FC0(v32);
      LOBYTE(v55) = 11;
      v34 = sub_4050C0(v37, v33);
      LOBYTE(v55) = 12;
      sub_4029D0(v34);
      CxxThrowException(pExceptionObject, (_ThrowInfo *)&_TI2_AVruntime_error_std__);
    }
    v21 += 48;
    result = ++v52;
    if ( v21 > 384 )
      return result;
    v19 = (_DWORD *)v51;
  }
}
```
## cleanup board data
```c
_DWORD *__thiscall sub_404A00(_DWORD *this, int a2)
{
  _DWORD *v3; // eax
  _DWORD *v4; // eax
  void *v5; // eax

  v3 = (_DWORD *)this[20];
  if ( v3 )
  {
    if ( ((this[22] - (_DWORD)v3) & 0xFFFFFFFC) >= 0x1000 )
    {
      if ( (unsigned int)v3 - *(v3 - 1) - 4 > 0x1F )
        goto LABEL_17;
      v3 = (_DWORD *)*(v3 - 1);
    }
    sub_49F7E3(v3);
    this[20] = 0;
    this[21] = 0;
    this[22] = 0;
  }
  v4 = (_DWORD *)this[7];
  if ( v4 )
  {
    if ( ((this[9] - (_DWORD)v4) & 0xFFFFFFFC) >= 0x1000 )
    {
      if ( (unsigned int)v4 - *(v4 - 1) - 4 > 0x1F )
        goto LABEL_17;
      v4 = (_DWORD *)*(v4 - 1);
    }
    sub_49F7E3(v4);
    this[7] = 0;
    this[8] = 0;
    this[9] = 0;
  }
  v5 = (void *)this[4];
  if ( v5 )
  {
    if ( ((this[6] - (_DWORD)v5) & 0xFFFFFFF8) < 0x1000 )
    {
LABEL_15:
      sub_49F7E3(v5);
      this[4] = 0;
      this[5] = 0;
      this[6] = 0;
      return this;
    }
    if ( (unsigned int)v5 - *((_DWORD *)v5 - 1) - 4 <= 0x1F )
    {
      v5 = (void *)*((_DWORD *)v5 - 1);
      goto LABEL_15;
    }
LABEL_17:
    invalid_parameter_noinfo_noreturn();
  }
  return this;
}```
## screen to world coords
```c
float *__thiscall sub_407DF0(char *this, float *a2, int a3, int a4)
{
  int *v4; // eax
  float v5; // xmm5_4
  float *v6; // ecx
  float *result; // eax
  float v8; // xmm3_4
  char v9[12]; // [esp+8h] [ebp-18h] BYREF
  float v10; // [esp+14h] [ebp-Ch]
  float v11; // [esp+18h] [ebp-8h]
  char *v12; // [esp+1Ch] [ebp-4h]

  v12 = this + 192;
  v10 = 0.0;
  v4 = (int *)sub_407B40(v9, this + 192);
  v5 = (float)((float)((float)((float)a4 - (float)v4[1]) * -2.0) / (float)v4[3]) + 1.0;
  v10 = (float)((float)((float)((float)a3 - (float)*v4) + (float)((float)a3 - (float)*v4)) / (float)v4[2]) - 1.0;
  v11 = v5;
  v6 = (float *)sub_408A40(v12);
  result = a2;
  v8 = (float)((float)(v11 * v6[5]) + (float)(v10 * v6[1])) + v6[13];
  *a2 = (float)((float)(v11 * v6[4]) + (float)(v10 * *v6)) + v6[12];
  a2[1] = v8;
  return result;
}
```
## update view matrix
```c
_BYTE *__thiscall sub_408220(_BYTE *this, const void *a2)
{
  _BYTE *result; // eax

  result = this;
  qmemcpy(this + 192, a2, 0xB8u);
  this[378] = 1;
  return result;
}
```
## transcode result
```c
_DWORD *__thiscall `anonymous namespace'::_Transcode_result::_Value(int *this, _DWORD *a2)
{
  _DWORD *result; // eax
  int v3; // edx
  int v4; // ecx

  result = a2;
  v3 = *this;
  v4 = this[1];
  *a2 = v3;
  a2[1] = v4;
  return result;
}
move camera position:
void __thiscall sub_408F10(float *this, float a2, float a3)
{
  float v3; // xmm1_4
  float v4; // xmm0_4

  v3 = a2 + *this;
  v4 = a3 + this[1];
  *((_WORD *)this + 90) = 0;
  *this = v3;
  this[1] = v4;
}
```
sprite initialization
```c
int __thiscall sub_409100(int this)
{
  sub_42B5A0();
  *(_DWORD *)(this + 8) = 0;
  *(_DWORD *)(this + 12) = 0;
  *(_DWORD *)(this + 16) = 0;
  *(_DWORD *)(this + 20) = 0;
  *(_DWORD *)(this + 24) = 0;
  *(_DWORD *)(this + 28) = 0;
  *(_WORD *)(this + 32) = 0;
  *(_QWORD *)(this + 40) = generate_unique_id();
  return this;
}
release sprite texture:
int __thiscall sub_4091C0(_DWORD *this)
{
  int result; // eax
  volatile signed __int32 *v3; // esi
  int v4; // [esp+Ch] [ebp-14h] BYREF
  char v5; // [esp+13h] [ebp-Dh] BYREF

  if ( this[6] )
  {
    sub_42B5C0(&v5);
    v4 = this[6];
    dword_4CE0A4(1, &v4);
    result = sub_42B5D0(&v5);
  }
  v3 = (volatile signed __int32 *)this[1];
  if ( v3 )
  {
    result = _InterlockedExchangeAdd(v3 + 1, 0xFFFFFFFF);
    if ( !result )
    {
      result = (**(int (__thiscall ***)(volatile signed __int32 *))v3)(v3);
      if ( !_InterlockedDecrement(v3 + 2) )
        return (*(int (__thiscall **)(volatile signed __int32 *))(*v3 + 4))(v3);
    }
  }
  return result;
}
generate unique id:
int sub_409730()
{
  __int64 v0; // rdi
  signed __int64 v1; // rax
  unsigned int i; // [esp+8h] [ebp-4h]

  v0 = qword_4CD038;
  for ( i = HIDWORD(qword_4CD038); ; i = HIDWORD(v1) )
  {
    v1 = _InterlockedCompareExchange64(&qword_4CD038, __PAIR64__((unsigned __int64)(v0 + 1) >> 32, v0) + 1, v0);
    if ( v1 == __PAIR64__(i, v0) )
      break;
    v0 = v1;
  }
  return v0;
}
```
## cell reveal
```c
// Reveals a cell at coordinates (x, y) and calculates mine adjacency.
//
// Structure layout:
// +0x00: board_width (DWORD)
// +0x04: board_height (DWORD)
// +0x10: board_data_ptr (Cell*) - pointer to 8-byte Cell structures
// +0x3C: random_seed (DWORD)
// +0x40: mine_width (DWORD)
// +0x44: mine_height (DWORD)
// +0x50: flood_fill_vector_start (DWORD*)
// +0x54: flood_fill_vector_current (DWORD*)
// +0x58: flood_fill_vector_end (DWORD*)
//
// Cell structure (8 bytes):
// +0: is_mine (BYTE)
// +1: is_revealed (BYTE)  
// +2: is_flagged (BYTE)
// +3: padding (BYTE)
// +4: adjacent_mine_count (DWORD)
//
// The mine generation uses a complex PRNG algorithm where mines are placed
// if: random_value % total_cells < 48 (mine probability threshold)
//
// Returns 1 if a mine was hit (game over), 0 otherwise.
char __thiscall reveal_cell(int *this, int a2, int a3)
{
  int v3; // ebx
  int v4; // eax
  int v5; // esi
  int v6; // edi
  int v8; // ebx
  int v9; // edi
  int v10; // esi
  int v11; // ebx
  int v12; // ebx
  int v13; // edi
  int v14; // esi
  int v15; // edx
  int v16; // edi
  int v17; // edx
  _BYTE *v18; // eax
  int v19; // [esp+Ch] [ebp-Ch]
  int v20; // [esp+10h] [ebp-8h]
  int v21; // [esp+10h] [ebp-8h]
  int v22; // [esp+14h] [ebp-4h]

  if ( a2 >= 0 )
  {
    v3 = a3;
    if ( a3 >= 0 && a2 < *this && a3 < this[1] )
    {
      v4 = this[4];
      v5 = a3 + a2 * *this;
      if ( !*(_BYTE *)(v4 + 8 * v5 + 1) && !*(_BYTE *)(v4 + 8 * v5 + 2) )
      {
        *(_BYTE *)(v4 + 8 * v5 + 1) = 1;
        v6 = this[16] * this[17] + 1;
        v20 = v3 + a2 * *this;
        if ( this[15] % v6 * (this[15] % v6 * (v20 % v6) % v6 % v6) % v6 < 48 )
        {
          *(_BYTE *)(this[4] + 8 * v20) = 1;
          return 1;
        }
        v21 = -1;
        v22 = a2 - 1;
        while ( 1 )
        {
          v8 = this[4] + 8 * (v3 + a2 * *this);
          v9 = this[15] % (this[16] * this[17] + 1);
          v10 = this[16] * this[17] + 1;
          *(_DWORD *)(v8 + 4) += v9 * (v9 * ((v22 * *this + a3 - 1) % v10) % v10 % v10) % v10 < 48;
          if ( v21 )
          {
            v19 = this[16] * this[17] + 1;
            v11 = this[4] + 8 * (a3 + a2 * *this);
            *(_DWORD *)(v11 + 4) += this[15] % v19 * (this[15] % v19 * ((a3 + v22 * *this) % v19) % v19 % v19) % v19 < 48;
          }
          v12 = this[4] + 8 * (a3 + a2 * *this);
          v13 = this[15] % (this[16] * this[17] + 1);
          v14 = this[16] * this[17] + 1;
          v15 = v13 * (v13 * ((v22 * *this + a3 + 1) % v14) % v14 % v14) % v14;
          ++v22;
          *(_DWORD *)(v12 + 4) += v15 < 48;
          if ( ++v21 > 1 )
            break;
          v3 = a3;
        }
        v16 = this[16] * this[17] + 1;
        v17 = this[15] % v16 * ((a3 + a2 * *this) % v16) % v16;
        v18 = (_BYTE *)this[21];
        a3 = v17;
        if ( v18 != (_BYTE *)this[22] )
        {
          *(_DWORD *)v18 = v17;
          this[21] += 4;
          return 0;
        }
        vector_insert_element((const void **)this + 20, v18, &a3);
      }
    }
  }
  return 0;
}
```
## vector insert element
```c
_DWORD *__thiscall sub_4025F0(const void **this, _BYTE *a2, _DWORD *a3)
{
  int v4; // eax
  unsigned int v5; // ecx
  unsigned int v6; // edx
  unsigned int v7; // ebx
  size_t v8; // ebx
  _DWORD *v9; // esi
  void *v10; // eax
  _BYTE *v11; // ecx
  const void *v12; // edx
  void *v13; // eax
  _DWORD *result; // eax
  int v15; // [esp-8h] [ebp-18h]
  int v16; // [esp-4h] [ebp-14h]
  int v17; // [esp+4h] [ebp-Ch]
  int v18; // [esp+Ch] [ebp-4h]
  _DWORD *v19; // [esp+Ch] [ebp-4h]

  v18 = (a2 - (_BYTE *)*this) >> 2;
  v4 = ((_BYTE *)this[1] - (_BYTE *)*this) >> 2;
  if ( v4 == 0x3FFFFFFF )
    sub_402560();
  v5 = ((_BYTE *)this[2] - (_BYTE *)*this) >> 2;
  v6 = v5 >> 1;
  v17 = v4 + 1;
  if ( v5 > 0x3FFFFFFF - (v5 >> 1) )
    goto LABEL_23;
  v7 = v4 + 1;
  if ( v6 + v5 >= v4 + 1 )
    v7 = v6 + v5;
  if ( v7 > 0x3FFFFFFF )
    goto LABEL_23;
  v8 = 4 * v7;
  if ( !v8 )
  {
    v9 = 0;
    goto LABEL_13;
  }
  if ( v8 < 0x1000 )
  {
    v9 = operator new(v8);
    goto LABEL_13;
  }
  if ( v8 + 35 < v8 )
LABEL_23:
    sub_401300(v15, v16);
  v10 = operator new(v8 + 35);
  if ( !v10 )
    goto LABEL_22;
  v9 = (_DWORD *)(((unsigned int)v10 + 35) & 0xFFFFFFE0);
  *(v9 - 1) = v10;
LABEL_13:
  v19 = &v9[v18];
  *v19 = *a3;
  v11 = this[1];
  v12 = *this;
  if ( a2 == v11 )
  {
    memmove(v9, v12, v11 - (_BYTE *)v12);
  }
  else
  {
    memmove(v9, v12, a2 - (_BYTE *)v12);
    memmove(v19 + 1, a2, (_BYTE *)this[1] - a2);
  }
  v13 = (void *)*this;
  if ( *this )
  {
    if ( (((_BYTE *)this[2] - (_BYTE *)v13) & 0xFFFFFFFC) < 0x1000 )
    {
LABEL_20:
      sub_49F7E3(v13);
      goto LABEL_21;
    }
    if ( (unsigned int)v13 - *((_DWORD *)v13 - 1) - 4 <= 0x1F )
    {
      v13 = (void *)*((_DWORD *)v13 - 1);
      goto LABEL_20;
    }
LABEL_22:
    invalid_parameter_noinfo_noreturn();
  }
LABEL_21:
  *this = v9;
  result = v19;
  this[1] = &v9[v17];
  this[2] = &v9[v8 / 4];
  return result;
}
```
## chacha20 initialization:
```c
// ChaCha20 initialization function
// Sets up the ChaCha20 state with:
// - 32-byte key loaded from a2
// - 8-byte nonce loaded from a3
// - Counter initialized to 0
//
// The state is arranged as a 4x4 matrix of 32-bit words.
_DWORD *__thiscall chacha20_init(_DWORD *this, unsigned __int8 *a2, unsigned __int8 *a3)
{
  int v4; // ecx
  _DWORD *result; // eax

  qmemcpy(this, "expand 32-byte k", 16);
  this[4] = *a2 | ((a2[1] | (*((unsigned __int16 *)a2 + 1) << 8)) << 8);
  this[5] = a2[4] | ((a2[5] | (*((unsigned __int16 *)a2 + 3) << 8)) << 8);
  this[6] = a2[8] | ((a2[9] | (*((unsigned __int16 *)a2 + 5) << 8)) << 8);
  this[7] = a2[12] | ((a2[13] | (*((unsigned __int16 *)a2 + 7) << 8)) << 8);
  this[8] = a2[16] | ((a2[17] | (*((unsigned __int16 *)a2 + 9) << 8)) << 8);
  this[9] = a2[20] | ((a2[21] | (*((unsigned __int16 *)a2 + 11) << 8)) << 8);
  this[10] = a2[24] | ((a2[25] | (*((unsigned __int16 *)a2 + 13) << 8)) << 8);
  v4 = a2[28] | ((a2[29] | (*((unsigned __int16 *)a2 + 15) << 8)) << 8);
  this[12] = 0;
  this[11] = v4;
  this[13] = 0;
  this[14] = *a3 | ((a3[1] | (*((unsigned __int16 *)a3 + 1) << 8)) << 8);
  result = this;
  this[15] = a3[4] | ((a3[5] | (*((unsigned __int16 *)a3 + 3) << 8)) << 8);
  return result;
}
```
## handle input events
```c
// Main input event handling function for the Minesweeper game.
//
// Handles two types of input:
// 1. Mouse input (v13 == 8):
//    - Converts screen coordinates to grid coordinates by dividing by 22
//    - Left click (v12[0] == 1): Reveals cell or sets game over flag
//    - Right click (v12[0] == 0): Toggles flag on unrevealed cells
//    
// 2. Keyboard input (v13 == 5):
//    - Key 73 ('I'): Game action
//    - Key 'H': Move camera left (-1045430272)
//    - Key 'G': Move camera right (1102053376)  
//    - Key 'J': Move camera down (1102053376)
//    
// The function checks game state (game_over at +588, remaining_cells at +568)
// and calls reveal_cell() for mouse clicks on valid grid positions.
//
// Grid coordinate calculation: (int)screen_x / 22, (int)screen_y / 22
// Cell size is 22x22 pixels in the graphics system.
int *__thiscall handle_input_events(int this)
{
  int *result; // eax
  char v3; // cl
  int v4; // ebx
  int *v5; // eax
  int v6; // esi
  int *v7; // eax
  int v8; // ecx
  int v9; // edx
  int v10; // ecx
  float v11; // [esp-4h] [ebp-3Ch]
  int v12[4]; // [esp+Ch] [ebp-2Ch] BYREF
  char v13; // [esp+1Ch] [ebp-1Ch]
  char v14; // [esp+20h] [ebp-18h]
  float v15; // [esp+24h] [ebp-14h] BYREF
  float v16; // [esp+28h] [ebp-10h]
  _BYTE v17[8]; // [esp+2Ch] [ebp-Ch] BYREF

  result = poll_input_events(*(int **)(this + 8), v12);
  if ( v14 )
  {
    while ( 1 )
    {
      v3 = v13;
      if ( !v13 || (result = (int *)v12[0], v13 == 5) && v12[0] == 36 )
      {
        (*(void (__thiscall **)(_DWORD))(**(_DWORD **)(this + 8) + 16))(*(_DWORD *)(this + 8));
        v3 = v13;
        result = (int *)v12[0];
      }
      if ( *(_BYTE *)(this + 588) || !*(_DWORD *)(this + 568) )
        return result;
      if ( v3 != 5 )
      {
        if ( v3 == 8 )
        {
          screen_to_world_coords((char *)(*(_DWORD *)(this + 8) + 56), &v15, v12[1], v12[2]);
          v4 = (int)v15 / 22;
          v5 = v12;
          v6 = (int)v16 / 22;
          if ( v13 != 8 )
            v5 = 0;
          if ( *v5 )
          {
            v7 = v12;
            if ( v13 != 8 )
              v7 = 0;
            if ( *v7 == 1 && v4 >= 0 && v6 >= 0 )
            {
              v8 = *(_DWORD *)(this + 492);
              if ( v4 < v8 && v6 < *(_DWORD *)(this + 496) )
              {
                v9 = *(_DWORD *)(this + 508);
                v10 = v6 + v4 * v8;
                if ( !*(_BYTE *)(v9 + 8 * v10 + 1) )
                  *(_BYTE *)(v9 + 8 * v10 + 2) = *(_BYTE *)(v9 + 8 * v10 + 2) == 0;
              }
            }
          }
          else if ( reveal_cell((int *)(this + 492), (int)v15 / 22, (int)v16 / 22) )
          {
            *(_BYTE *)(this + 588) = 1;
          }
          else
          {
            --*(_DWORD *)(this + 568);
          }
        }
        goto LABEL_35;
      }
      if ( result == (int *)73 )
        break;
      if ( result == (int *)72 )
      {
        move_camera_position((float *)(this + 308), 22.0, 0.0);
      }
      else if ( result == (int *)71 )
      {
        move_camera_position((float *)(this + 308), -22.0, 0.0);
      }
      else if ( result == (int *)74 )
      {
        v11 = 22.0;
        goto LABEL_17;
      }
LABEL_18:
      `anonymous namespace'::_Transcode_result::_Value(v17);
      update_view_matrix((_BYTE *)(*(_DWORD *)(this + 8) + 56), (const void *)(this + 308));
LABEL_35:
      result = poll_input_events(*(int **)(this + 8), v12);
      if ( !v14 )
        return result;
    }
    v11 = -22.0;
LABEL_17:
    move_camera_position((float *)(this + 308), 0.0, v11);
    goto LABEL_18;
  }
  return result;
}
check game state and display result:
// Game state checker and result display function
//
// Checks three game states:
// 1. Player lost (this+588 is true): Display "You Lose!" in red
// 2. Game in progress (this+568 is non-zero): Continue game
// 3. Player won (this+568 is 0):
//    - Call feistel_network_and_chacha20 to decrypt flag
//    - Display decrypted flag in green
//    - Set flag at this+589 to prevent re-decryption
//
// The decrypted flag is stored at this+590 after successful validation.
int __thiscall check_game_state_and_display_result(int this)
{
  void (__thiscall ***v2)(_DWORD, int); // eax
  void *v3; // edx
  unsigned int v4; // ecx
  void (__thiscall ***v5)(_DWORD, int); // eax
  _DWORD *v6; // eax
  float v7; // xmm2_4
  void (__thiscall ***v8)(_DWORD, int); // eax
  void *v9; // edx
  unsigned int v10; // ecx
  void (__thiscall ***v11)(_DWORD, int); // eax
  _DWORD *v12; // eax
  float v13; // xmm2_4
  void *v15; // [esp-1Ch] [ebp-1A8h] BYREF
  int v16; // [esp-18h] [ebp-1A4h]
  int v17; // [esp-14h] [ebp-1A0h]
  int v18; // [esp-10h] [ebp-19Ch]
  __int64 v19; // [esp-Ch] [ebp-198h]
  unsigned int v20; // [esp-4h] [ebp-190h]
  void *Block[4]; // [esp+Ch] [ebp-180h] BYREF
  int v22; // [esp+1Ch] [ebp-170h]
  unsigned int v23; // [esp+20h] [ebp-16Ch]
  int v24; // [esp+24h] [ebp-168h] BYREF
  struct std::locale::_Locimp *v25; // [esp+28h] [ebp-164h]
  int v26; // [esp+2Ch] [ebp-160h] BYREF
  struct std::locale::_Locimp *v27; // [esp+30h] [ebp-15Ch]
  _DWORD v28[2]; // [esp+34h] [ebp-158h] BYREF
  int v29; // [esp+3Ch] [ebp-150h] BYREF
  struct std::locale::_Locimp *v30; // [esp+40h] [ebp-14Ch]
  __int64 v31[37]; // [esp+44h] [ebp-148h] BYREF
  _BYTE v32[16]; // [esp+16Ch] [ebp-20h] BYREF
  int v33; // [esp+188h] [ebp-4h]
  int savedregs; // [esp+18Ch] [ebp+0h] BYREF

  sub_4075D0(0);
  if ( *(_BYTE *)(this + 588) )
  {
    memset(v31, 0, sizeof(v31));
    v33 = 0;
    v25 = std::locale::_Init(1);
    v20 = 30;
    v33 = 1;
    sub_438B30(&v15, (int)&WindowName, (int)&v24);
    sub_40CE20(this + 24, v15, v16, v17, v18, v19, v20);
    LOBYTE(v33) = 3;
    if ( v25 )
    {
      v2 = (void (__thiscall ***)(_DWORD, int))(*(int (__thiscall **)(struct std::locale::_Locimp *))(*(_DWORD *)v25 + 8))(v25);
      if ( v2 )
        (**v2)(v2, 1);
    }
    sub_40DF90(48);
    sub_40DFB0(-16776961);
    sub_40E1A0(1);
    LOBYTE(v33) = 4;
    v27 = std::locale::_Init(1);
    LOBYTE(v33) = 5;
    sub_438B30(Block, (int)"You Lose!", (int)&v26);
    LOBYTE(v33) = 6;
    sub_40E050(Block);
    LOBYTE(v33) = 5;
    if ( v23 > 3 )
    {
      v3 = Block[0];
      v4 = 4 * v23 + 4;
      if ( v4 >= 0x1000 )
      {
        v3 = (void *)*((_DWORD *)Block[0] - 1);
        v4 = 4 * v23 + 39;
        if ( (unsigned int)((char *)Block[0] - (char *)v3 - 4) > 0x1F )
          invalid_parameter_noinfo_noreturn();
      }
      v20 = v4;
      sub_49F7E3(v3);
    }
    LOBYTE(v33) = 3;
    v22 = 0;
    v23 = 3;
    Block[0] = 0;
    if ( v27 )
    {
      v5 = (void (__thiscall ***)(_DWORD, int))(*(int (__thiscall **)(struct std::locale::_Locimp *))(*(_DWORD *)v27 + 8))(v27);
      if ( v5 )
        (**v5)(v5, 1);
    }
    sub_40DF70(v32);
    (*(void (__thiscall **)(int, int *))(*(_DWORD *)(*(_DWORD *)(this + 8) + 56) + 4))(*(_DWORD *)(this + 8) + 56, &v29);
    v6 = (_DWORD *)sub_404DF0(v28);
    sub_4063A0(*v6, v6[1]);
    v7 = (float)(unsigned int)v30;
    sub_4063C0((float)(unsigned int)v29 * 0.5, v7 * 0.5);
    sub_4076C0(v31, &xmmword_4CDAB8);
    v33 = -1;
    sub_4046C0(v31);
  }
  else if ( *(_DWORD *)(this + 568) )
  {
    if ( *(_BYTE *)(this + 584) )
      sub_4018E0(*(_DWORD *)(this + 8));
  }
  else
  {
    if ( !*(_BYTE *)(this + 589) )
    {
      feistel_network_and_chacha20((_DWORD *)(this + 492), (int)&savedregs, (void *)(this + 590));
      *(_BYTE *)(this + 589) = 1;
    }
    memset(v31, 0, sizeof(v31));
    v33 = 7;
    v30 = std::locale::_Init(1);
    v20 = 30;
    v33 = 8;
    sub_438B30(&v15, (int)&WindowName, (int)&v29);
    sub_40CE20(this + 24, v15, v16, v17, v18, v19, v20);
    LOBYTE(v33) = 10;
    if ( v30 )
    {
      v8 = (void (__thiscall ***)(_DWORD, int))(*(int (__thiscall **)(struct std::locale::_Locimp *))(*(_DWORD *)v30 + 8))(v30);
      if ( v8 )
        (**v8)(v8, 1);
    }
    sub_40DF90(16);
    sub_40DFB0(-16711936);
    sub_40E1A0(1);
    LOBYTE(v33) = 11;
    v25 = std::locale::_Init(1);
    LOBYTE(v33) = 12;
    sub_438B30(Block, this + 590, (int)&v24);
    LOBYTE(v33) = 13;
    sub_40E050(Block);
    LOBYTE(v33) = 12;
    if ( v23 > 3 )
    {
      v9 = Block[0];
      v10 = 4 * v23 + 4;
      if ( v10 >= 0x1000 )
      {
        v9 = (void *)*((_DWORD *)Block[0] - 1);
        v10 = 4 * v23 + 39;
        if ( (unsigned int)((char *)Block[0] - (char *)v9 - 4) > 0x1F )
          invalid_parameter_noinfo_noreturn();
      }
      v20 = v10;
      sub_49F7E3(v9);
    }
    LOBYTE(v33) = 10;
    v22 = 0;
    v23 = 3;
    Block[0] = 0;
    if ( v25 )
    {
      v11 = (void (__thiscall ***)(_DWORD, int))(*(int (__thiscall **)(struct std::locale::_Locimp *))(*(_DWORD *)v25 + 8))(v25);
      if ( v11 )
        (**v11)(v11, 1);
    }
    sub_40DF70(v32);
    (*(void (__thiscall **)(int, _DWORD *))(*(_DWORD *)(*(_DWORD *)(this + 8) + 56) + 4))(
      *(_DWORD *)(this + 8) + 56,
      v28);
    v12 = (_DWORD *)sub_404DF0(&v24);
    sub_4063A0(*v12, v12[1]);
    v13 = (float)v28[1];
    sub_4063C0((float)v28[0] * 0.5, v13 * 0.5);
    sub_4076C0(v31, &xmmword_4CDAB8);
    v33 = -1;
    sub_4046C0(v31);
  }
  return sub_42AEE0(*(_DWORD *)(this + 8));
}
cleanup sprite vector:
int __thiscall cleanup_sprite_vector(_DWORD *this)
{
  _DWORD *v2; // esi
  _DWORD *i; // ebx
  _DWORD *v4; // esi
  int result; // eax

  v2 = (_DWORD *)*this;
  if ( *this )
  {
    for ( i = (_DWORD *)this[1]; v2 != i; v2 += 12 )
      release_sprite_texture(v2);
    v4 = (_DWORD *)*this;
    if ( (unsigned int)(48 * ((this[2] - *this) / 48)) >= 0x1000 )
    {
      if ( (unsigned int)v4 - *(v4 - 1) - 4 > 0x1F )
        invalid_parameter_noinfo_noreturn();
      v4 = (_DWORD *)*(v4 - 1);
    }
    result = sub_49F7E3(v4);
    *this = 0;
    this[1] = 0;
    this[2] = 0;
  }
  return result;
}
```c
## chacha20 block
```c
// ChaCha20 block function
// Generates a 64-byte keystream block:
// 1. Copies initial state to working state
// 2. Performs 10 double-rounds (20 quarter-rounds total)
// 3. Adds initial state to working state
// 4. Increments block counter
// 5. Outputs 64 bytes of keystream in a2
//
// Each quarter-round performs:
// a += b; d ^= a; d <<<= 16;
// c += d; b ^= c; b <<<= 12;
// a += b; d ^= a; d <<<= 8;
// c += d; b ^= c; b <<<= 7;
__m128i *__thiscall chacha20_block(__m128i *this, __m128i *a2)
{
  __int32 v2; // ebx
  __int32 v3; // esi
  __int32 v4; // edi
  unsigned int v5; // eax
  __int32 v6; // ecx
  unsigned int v7; // edx
  unsigned __int32 v8; // edi
  unsigned int v9; // esi
  unsigned int v10; // edx
  unsigned int v11; // esi
  unsigned int v12; // edx
  unsigned int v13; // esi
  unsigned int v14; // edx
  unsigned __int32 v15; // edi
  unsigned int v16; // esi
  unsigned int v17; // edx
  unsigned int v18; // ebx
  unsigned __int32 v19; // edi
  unsigned int v20; // edx
  __int32 v21; // eax
  int v22; // edx
  unsigned int v23; // edi
  unsigned int v24; // edx
  unsigned int v25; // edi
  unsigned int v26; // ebx
  unsigned int v27; // esi
  unsigned int v28; // edx
  int v29; // ebx
  __m128i *result; // eax
  __m128i v31; // xmm0
  __m128i v32; // xmm1
  __m128i v33; // xmm0
  unsigned __int32 v35; // [esp+4h] [ebp-58h]
  unsigned int v36; // [esp+8h] [ebp-54h]
  unsigned int v37; // [esp+Ch] [ebp-50h]
  unsigned __int32 v38; // [esp+10h] [ebp-4Ch]
  unsigned __int32 v39; // [esp+14h] [ebp-48h]
  unsigned int v40; // [esp+18h] [ebp-44h]
  unsigned __int32 v41; // [esp+1Ch] [ebp-40h]
  __int32 v42; // [esp+1Ch] [ebp-40h]
  unsigned __int32 v43; // [esp+20h] [ebp-3Ch]
  __int32 v44; // [esp+20h] [ebp-3Ch]
  int v45; // [esp+24h] [ebp-38h]
  __int32 v46; // [esp+28h] [ebp-34h]
  unsigned int v47; // [esp+28h] [ebp-34h]
  __int32 v48; // [esp+2Ch] [ebp-30h]
  unsigned __int32 v49; // [esp+2Ch] [ebp-30h]
  unsigned int v50; // [esp+30h] [ebp-2Ch]
  __int32 v51; // [esp+34h] [ebp-28h]
  unsigned int v52; // [esp+34h] [ebp-28h]
  __int32 v53; // [esp+38h] [ebp-24h]
  __int32 v54; // [esp+3Ch] [ebp-20h]
  __int32 v55; // [esp+40h] [ebp-1Ch]
  unsigned __int32 v56; // [esp+40h] [ebp-1Ch]
  __int32 v57; // [esp+44h] [ebp-18h]
  __int32 v58; // [esp+48h] [ebp-14h]
  unsigned int v59; // [esp+48h] [ebp-14h]
  unsigned int v60; // [esp+4Ch] [ebp-10h]
  unsigned __int32 v61; // [esp+4Ch] [ebp-10h]
  __int32 v62; // [esp+50h] [ebp-Ch]
  __int32 v63; // [esp+54h] [ebp-8h]
  unsigned int v64; // [esp+58h] [ebp-4h]
  __int32 v65; // [esp+58h] [ebp-4h]

  *a2 = *this;
  v45 = 10;
  a2[1] = this[1];
  a2[2] = this[2];
  a2[3] = this[3];
  v2 = a2[1].m128i_i32[0];
  v3 = a2->m128i_i32[0];
  v4 = a2[2].m128i_i32[0];
  v63 = a2[1].m128i_i32[1];
  v62 = a2->m128i_i32[1];
  v60 = a2[3].m128i_u32[1];
  v58 = a2[2].m128i_i32[1];
  v57 = a2[1].m128i_i32[2];
  v55 = a2->m128i_i32[2];
  v46 = a2[3].m128i_i32[2];
  v54 = a2[2].m128i_i32[2];
  v53 = a2[1].m128i_i32[3];
  v51 = a2->m128i_i32[3];
  v50 = a2[3].m128i_u32[3];
  v5 = a2[3].m128i_u32[0];
  v48 = a2[2].m128i_i32[3];
  do
  {
    v6 = v2 + v3;
    v7 = (((v2 + v3) ^ v5) << 16) | (((v2 + v3) ^ v5) >> 16);
    v8 = v7 + v4;
    v9 = ((v8 ^ v2) << 12) | ((v8 ^ v2) >> 20);
    v41 = v9 + v6;
    v64 = ((v7 ^ (v9 + v6)) << 8) | ((v7 ^ (v9 + v6)) >> 24);
    v39 = v8 + v64;
    v37 = ((v9 ^ v39) << 7) | ((v9 ^ v39) >> 25);
    v10 = (((v63 + v62) ^ v60) << 16) | (((v63 + v62) ^ v60) >> 16);
    v11 = (((v10 + v58) ^ v63) << 12) | (((v10 + v58) ^ v63) >> 20);
    v61 = v11 + v63 + v62;
    v40 = ((v10 ^ v61) << 8) | ((v10 ^ v61) >> 24);
    v35 = v10 + v58 + v40;
    v59 = ((v11 ^ v35) << 7) | ((v11 ^ v35) >> 25);
    v12 = ((v46 ^ (v57 + v55)) << 16) | ((v46 ^ (unsigned int)(v57 + v55)) >> 16);
    v13 = (((v12 + v54) ^ v57) << 12) | (((v12 + v54) ^ v57) >> 20);
    v56 = v57 + v55 + v13;
    v36 = ((v12 ^ v56) << 8) | ((v12 ^ v56) >> 24);
    v43 = v12 + v54 + v36;
    v47 = ((v13 ^ v43) << 7) | ((v13 ^ v43) >> 25);
    v14 = (((v53 + v51) ^ v50) << 16) | (((v53 + v51) ^ v50) >> 16);
    v15 = v14 + v48;
    v16 = (((v14 + v48) ^ v53) << 12) | (((v14 + v48) ^ v53) >> 20);
    v38 = v16 + v53 + v51;
    v17 = ((v14 ^ v38) << 8) | ((v14 ^ v38) >> 24);
    v49 = v15 + v17;
    v52 = ((v16 ^ (v15 + v17)) << 7) | ((v16 ^ (v15 + v17)) >> 25);
    v18 = ((v17 ^ (v59 + v41)) << 16) | ((v17 ^ (v59 + v41)) >> 16);
    v19 = v18 + v43;
    v20 = ((v59 ^ (v18 + v43)) << 12) | ((v59 ^ (v18 + v43)) >> 20);
    v44 = v59 + v41 + v20;
    v50 = ((v44 ^ v18) << 8) | ((v44 ^ v18) >> 24);
    v54 = v19 + v50;
    v21 = __ROR4__(v20 ^ (v19 + v50), 25);
    v22 = ((v64 ^ (v47 + v61)) << 16) | ((unsigned __int64)(v64 ^ (v47 + v61)) >> 16);
    v63 = v21;
    v23 = ((v47 ^ (v22 + v49)) << 12) | ((v47 ^ (v22 + v49)) >> 20);
    v62 = v47 + v61 + v23;
    v5 = ((v22 ^ v62) << 8) | ((v22 ^ (unsigned int)v62) >> 24);
    v48 = v22 + v49 + v5;
    v57 = __ROR4__(v23 ^ v48, 25);
    v24 = ((v40 ^ (v52 + v56)) << 16) | ((v40 ^ (v52 + v56)) >> 16);
    v25 = ((v52 ^ (v24 + v39)) << 12) | ((v52 ^ (v24 + v39)) >> 20);
    v55 = v52 + v56 + v25;
    v60 = ((v24 ^ v55) << 8) | ((v24 ^ v55) >> 24);
    v42 = v24 + v39 + v60;
    v53 = __ROR4__(v25 ^ v42, 25);
    v26 = (((v37 + v38) ^ v36) << 16) | (((v37 + v38) ^ v36) >> 16);
    v27 = ((v37 ^ (v26 + v35)) << 12) | ((v37 ^ (v26 + v35)) >> 20);
    v51 = v27 + v37 + v38;
    v28 = ((v51 ^ v26) << 8) | ((v51 ^ v26) >> 24);
    v46 = v28;
    v4 = v42;
    v58 = v26 + v35 + v28;
    v29 = v27 ^ v58;
    v3 = v44;
    v2 = __ROR4__(v29, 25);
    --v45;
  }
  while ( v45 );
  v65 = v5;
  result = a2;
  a2[3].m128i_i32[0] = v65;
  a2[1].m128i_i32[1] = v63;
  a2->m128i_i32[1] = v62;
  a2[3].m128i_i32[1] = v60;
  a2[1].m128i_i32[2] = v57;
  a2->m128i_i32[2] = v55;
  a2[2].m128i_i32[2] = v54;
  a2[1].m128i_i32[3] = v53;
  a2->m128i_i32[3] = v51;
  a2->m128i_i32[0] = v44;
  a2[3].m128i_i32[3] = v50;
  a2[2].m128i_i32[1] = v58;
  a2[1].m128i_i32[0] = v2;
  v31 = *this;
  a2[2].m128i_i32[0] = v42;
  a2[2].m128i_i32[3] = v48;
  a2[3].m128i_i32[2] = v28;
  *a2 = _mm_add_epi32(*a2, v31);
  a2[1] = _mm_add_epi32(this[1], a2[1]);
  v32 = _mm_add_epi32(this[2], a2[2]);
  v33 = this[3];
  ++this[3].m128i_i32[0];
  a2[2] = v32;
  a2[3] = _mm_add_epi32(a2[3], v33);
  if ( v33.m128i_i32[0] == -1 )
    ++this[3].m128i_i32[1];
  return result;
}
```
## feistel modular multiply
```c
// Modular multiplication for Feistel network
// Computes: (a * b) mod 1123996597
//
// This is used in the Feistel rounds to combine the key with data.
// The modulo value 1123996597 is a prime number used to ensure
// good mixing properties in the Feistel network.
__int64 __stdcall feistel_modular_multiply(int a1, int a2)
{
  unsigned int v2; // ecx

  v2 = 1123996597
     * (((int)((unsigned __int64)(512866991LL * a2) >> 32) >> 27)
      + ((unsigned int)((unsigned __int64)(512866991LL * a2) >> 32) >> 31));
  return ((int)(a2 - v2)
        * (__int64)(int)(a1
                       - 1123996597
                       * (((int)((unsigned __int64)(512866991LL * a1) >> 32) >> 27)
                        + ((unsigned int)((unsigned __int64)(512866991LL * a1) >> 32) >> 31)))
        + a2
        - 1123996597
        * ((int)(a2 - v2)
         * (__int64)(int)(a1
                        - 1123996597
                        * (((int)((unsigned __int64)(512866991LL * a1) >> 32) >> 27)
                         + ((unsigned int)((unsigned __int64)(512866991LL * a1) >> 32) >> 31)))
         / 1123996597))
       % 1123996597;
}
```
## feistel network and chacha20
```c
// Feistel Network and ChaCha20 Encryption Function
//
// This function performs:
// 1. Extract 16 bytes from input and arrange into 4 DWORDs
// 2. Run 10 rounds of modified Feistel network:
//    - Uses modulo 1123996597 (0x42FED3B5)
//    - Generates keys using: key = (key * seed) % (width*height+1)
//    - Each round performs XOR and modular multiplication operations
// 3. Check if result matches target values:
//    - 342066253 (0x1468230D)
//    - 686880041 (0x28EE5D29)
//    - 828922036 (0x316D90B4)
//    - 509868183 (0x1E602C97)
// 4. If match found:
//    - Generate ChaCha20 key from pressed cells
//    - First 16 bytes: original pressed values
//    - Next 16 bytes: mapped values using valueChecker
//    - Decrypt the flag using ChaCha20 with nonce 0xf0679036f2a46517
void *__userpurge feistel_network_and_chacha20@<eax>(_DWORD *a1@<ecx>, int a2@<ebp>, void *a3)
{
  _DWORD *v3; // edx
  _DWORD *v4; // edi
  char *v5; // ecx
  int v6; // eax
  int v7; // edx
  int v8; // esi
  _DWORD *v9; // ecx
  int v10; // kr00_4
  unsigned int v11; // eax
  int v12; // ecx
  int v13; // esi
  int v14; // edi
  int v15; // ecx
  int v16; // edi
  unsigned __int64 v17; // kr08_8
  int v18; // eax
  int v19; // eax
  void *result; // eax
  int v21; // edi
  int v22; // ecx
  int v23; // esi
  int v24; // edx
  _BYTE *v25; // eax
  _BYTE *v26; // edx
  int i; // ecx
  _DWORD *v28; // esi
  int v29; // edi
  bool v30; // zf
  int v31; // edi
  int v32; // eax
  unsigned int v33; // edi
  unsigned int v34; // edx
  unsigned int v35; // ecx
  _DWORD *v36; // esi
  unsigned int j; // eax
  __int32 v38; // edx
  __int32 v39; // edx
  __int32 v40; // edx
  __int32 v41; // edx
  int v42; // edi
  char *v43; // eax
  int v44; // [esp-148h] [ebp-154h]
  int v45; // [esp-144h] [ebp-150h]
  int v46; // [esp-140h] [ebp-14Ch]
  int v47; // [esp-128h] [ebp-134h]
  int v48; // [esp-128h] [ebp-134h]
  _DWORD v49[3]; // [esp-124h] [ebp-130h] BYREF
  __int128 v50; // [esp-118h] [ebp-124h]
  __int16 v51; // [esp-108h] [ebp-114h]
  _DWORD *v52; // [esp-104h] [ebp-110h]
  int v53; // [esp-100h] [ebp-10Ch]
  int v54; // [esp-FCh] [ebp-108h]
  __m128i v55[3]; // [esp-F8h] [ebp-104h] BYREF
  int v56; // [esp-C8h] [ebp-D4h]
  int v57; // [esp-C4h] [ebp-D0h]
  _BYTE v58[64]; // [esp-B8h] [ebp-C4h]
  int v59; // [esp-78h] [ebp-84h]
  __m128i v60[2]; // [esp-70h] [ebp-7Ch] BYREF
  unsigned __int8 v61[36]; // [esp-50h] [ebp-5Ch] BYREF
  __int128 v62; // [esp-2Ch] [ebp-38h]
  int v63; // [esp-1Ch] [ebp-28h] BYREF
  _DWORD v64[5]; // [esp-18h] [ebp-24h] BYREF
  int v65; // [esp-4h] [ebp-10h]
  int v66; // [esp+0h] [ebp-Ch]
  void *v67; // [esp+4h] [ebp-8h]
  int v68; // [esp+8h] [ebp-4h] BYREF
  void *retaddr; // [esp+Ch] [ebp+0h]

  v66 = a2;
  v67 = retaddr;
  v65 = -1;
  v64[4] = &loc_4A0F1D;
  v64[3] = NtCurrentTeb()->NtTib.ExceptionList;
  v64[2] = &v68;
  v3 = a1;
  v52 = a1;
  v4 = a1 + 20;
  v5 = 0;
  v6 = (v4[1] - *v4) >> 2;
  v53 = 0;
  v64[0] = v4;
  if ( v6 )
  {
    do
    {
      v7 = 0;
      v54 = (int)(v5 + 4);
      v8 = (int)v5;
      if ( !__OFSUB__(v5, v5 + 4) )
      {
        do
        {
          v9 = (_DWORD *)(*v4 + 4 * v8++);
          v7 = *v9 | (v7 << 8);
          *v9 = (unsigned __int8)*v9;
        }
        while ( v8 < v54 );
        v5 = (char *)v53;
      }
      v10 = (int)v5;
      v5 = (char *)v54;
      *((_DWORD *)&v62 + v10 / 4) = v7;
      v11 = (v4[1] - *v4) >> 2;
      v53 = (int)v5;
    }
    while ( (unsigned int)v5 < v11 );
    v3 = v52;
  }
  v12 = 10;
  v13 = v3[3];
  v14 = v3[16] * v3[17] + 1;
  v45 = v14;
  v47 = v3[15] % v14;
  do
  {
    v46 = v12 - 1;
    v15 = 0xABFDFFBD;
    do
    {
      v13 = v47 * (v13 % v14) % v14;
      --v15;
    }
    while ( v15 );
    v53 = DWORD2(v62);
    v17 = *(_QWORD *)((char *)&v62 + 4);
    LODWORD(v50) = HIDWORD(v17);
    v16 = v17;
    v18 = feistel_modular_multiply(SDWORD1(v62), v13);
    v44 = v62 ^ v18;
    DWORD1(v50) = v62 ^ v18;
    v54 = (int)(v62
              - 1123996597
              * (((int)((unsigned __int64)(512866991LL * (int)v62) >> 32) >> 27)
               + ((unsigned int)((unsigned __int64)(512866991LL * (int)v62) >> 32) >> 31))) >> 31;
    DWORD2(v50) = (int)(v53
                      - 1123996597
                      * (((int)((unsigned __int64)(512866991LL * v53) >> 32) >> 27)
                       + ((unsigned int)((unsigned __int64)(512866991LL * v53) >> 32) >> 31)))
                * (__int64)(int)(v62
                               - 1123996597
                               * (((int)((unsigned __int64)(512866991LL * (int)v62) >> 32) >> 27)
                                + ((unsigned int)((unsigned __int64)(512866991LL * (int)v62) >> 32) >> 31)))
                % 1123996597
                * ((v16 + (__int64)SHIDWORD(v62)) % 1123996597)
                % 1123996597;
    v19 = feistel_modular_multiply(v53, v13);
    result = (void *)(DWORD1(v62) ^ v19);
    v12 = v46;
    HIDWORD(v50) = result;
    v62 = v50;
    v14 = v45;
  }
  while ( v46 );
  if ( v53 == 342066253 && v44 == 686880041 && DWORD2(v50) == 828922036 && result == (void *)509868183 )
  {
    v21 = v64[0];
    v22 = 0;
    v54 = 0;
    do
    {
      v23 = v52[17] * v52[16] + 1;
      v24 = v52[15] % v23 * (*(_DWORD *)(v22 + *(_DWORD *)v21) % v23) % v23;
      v25 = *(_BYTE **)(v21 + 4);
      v64[0] = v24;
      if ( v25 == *(_BYTE **)(v21 + 8) )
      {
        vector_insert_element((const void **)v21, v25, v64);
      }
      else
      {
        *(_DWORD *)v25 = v24;
        *(_DWORD *)(v21 + 4) += 4;
      }
      v22 = v54 + 4;
      v54 = v22;
    }
    while ( v22 < 64 );
    v26 = *(_BYTE **)v21;
    for ( i = 0; i < 32; i += 4 )
    {
      v61[i] = v26[4 * i];
      v61[i + 1] = v26[4 * i + 4];
      v61[i + 2] = v26[4 * i + 8];
      v61[i + 3] = v26[4 * i + 12];
    }
    v63 = 0x369067F0;
    v64[0] = 0x1765A4F2;
    *(_QWORD *)((char *)&v62 + 4) = 0LL;
    v49[0] = 0x7BEABC84;                        // v49 - v51 is the ciphertext
    v49[1] = 0x18418440;
    v49[2] = 0xDBCBC705;
    *(_QWORD *)&v50 = 0x9185733C62E8F422uLL;
    *((_QWORD *)&v50 + 1) = 0x4DCD4172D214DB83LL;
    v51 = 200;
    HIDWORD(v62) = 0;
    v52 = 0;
    v53 = (int)operator new(0x1Eu);
    v28 = (_DWORD *)v53;
    DWORD1(v62) = v53;
    v54 = v53 + 30;
    HIDWORD(v62) = v53 + 30;
    memmove((void *)v53, v49, 0x1Eu);
    DWORD2(v62) = v54;
    v29 = v54;
    v52 = v28;
    v65 = 0;
    chacha20_init(v55, v61, (unsigned __int8 *)&v63);
    v31 = v29 - (_DWORD)v52;
    v30 = v31 == 0;
    v32 = 64;
    v48 = v31;
    v59 = 64;
    v56 = 0;
    v57 = 0;
    v33 = 0;
    if ( !v30 )
    {
      v34 = v48;
      v35 = 64;
      v36 = v52;
      do
      {
        if ( v35 >= 0x40 )
        {
          chacha20_block(v55, v60);
          for ( j = 0; j < 0x40; j += 16 )
          {
            v38 = v60[j / 0x10].m128i_i32[0];
            *(_WORD *)&v58[j] = v38;
            v58[j + 2] = BYTE2(v38);
            v58[j + 3] = HIBYTE(v38);
            v39 = v60[j / 0x10].m128i_i32[1];
            *(_WORD *)&v58[j + 4] = v39;
            v58[j + 6] = BYTE2(v39);
            v58[j + 7] = HIBYTE(v39);
            v40 = v60[j / 0x10].m128i_i32[2];
            *(_WORD *)&v58[j + 8] = v40;
            v58[j + 10] = BYTE2(v40);
            v58[j + 11] = HIBYTE(v40);
            v41 = v60[j / 0x10].m128i_i32[3];
            *(_WORD *)&v58[j + 12] = v41;
            v58[j + 14] = BYTE2(v41);
            v58[j + 15] = HIBYTE(v41);
          }
          v34 = v48;
          v32 = 0;
          v59 = 0;
        }
        *((_BYTE *)v36 + v33++) ^= v58[v32];
        v32 = v59 + 1;
        v59 = v32;
        v35 = v32;
      }
      while ( v33 < v34 );
      v28 = (_DWORD *)v53;
    }
    v42 = v54 - (_DWORD)v28;
    memcpy(a3, v28, v54 - (_DWORD)v28);
    result = a3;
    *((_BYTE *)a3 + v42) = 0;
    if ( v28 )
    {
      v43 = (char *)v28;
      if ( (unsigned int)(HIDWORD(v62) - (_DWORD)v28) >= 0x1000 )
      {
        v28 = (_DWORD *)*(v28 - 1);
        if ( (unsigned int)(v43 - (char *)v28 - 4) > 0x1F )
          invalid_parameter_noinfo_noreturn();
      }
      return (void *)((int (__cdecl *)(void *))sub_49F7E3)(v28);
    }
  }
  return result;
}
```
The game creates an 8x8 grid with an unusual mine density of 48 mines out of 64 total cells, making it significantly more challenging than traditional minesweeper. The program uses a complex pseudo-random number generator (PRNG) with a fixed seed value of 88403651 to deterministically place mines based on a mathematical formula that checks if a calculated value modulo the total cells is less than 48.

The game's input handling system processes both mouse and keyboard events through the `handle_input_events` function. Mouse clicks are converted from screen coordinates to grid coordinates by dividing by 22 (the pixel size of each cell). Left clicks reveal cells while right clicks toggle flags on unrevealed cells. The keyboard controls include H, G, and J keys for camera movement and the I key for game actions. The game maintains its state through several key variables, including a game-over flag and a remaining cells counter.

The reveal mechanism is particularly interesting as it combines traditional minesweeper logic with the cryptographic challenge. When a cell is revealed, the `reveal_cell` function not only checks for mines but also calculates adjacent mine counts using the same PRNG algorithm. The function implements a flood-fill algorithm for revealing empty cells, storing coordinates in a vector for processing. Each cell in the game is represented by an 8-byte structure containing flags for whether it's a mine, revealed, or flagged, along with the adjacent mine count.

The win condition checking in `check_game_state_and_display_result` reveals the true purpose of this challenge. When the player successfully clears all non-mine cells (reducing the remaining cell count to 0), the game calls the `feistel_network_and_chacha20` function to decrypt a hidden flag. This decryption process extracts 16 bytes from the game state, runs them through 10 rounds of a modified Feistel network using modular arithmetic with the constant 1123996597, and checks if the result matches specific target values (342066253, 686880041, 828922036, and 509868183).

Upon achieving the correct game state, the program generates a ChaCha20 encryption key from the pressed cell values and uses it to decrypt a hardcoded ciphertext with the nonce 0xf0679036f2a46517. The decrypted flag is then displayed in green text to indicate victory, while losses are shown with "You Lose!" in red. 

I create a solver that bypasses the minesweeper game entirely by reversing the cryptographic operations to directly obtain the flag. Instead of playing the extremely difficult game with 48 mines in 64 cells, I recognize that the win condition checks if a 10-round Feistel network produces four specific target values (342066253, 686880041, 828922036, 509868183). My solver implements the inverse Feistel operations, working backwards through each round using modular multiplicative inverses to recover the original 16-byte input state from these known outputs. I then replicate the game's key expansion algorithm, which transforms the 16 bytes into a 32-byte ChaCha20 key by appending each byte multiplied by the seed modulo 65. Finally, I decrypt the hardcoded ciphertext using the recovered chacha20 key and the known nonce. 

## Solver
```python
#!/usr/bin/env python3

from Crypto.Util.number import inverse
from Crypto.Cipher import ChaCha20

class FeistelChaCha20Decryptor:
    def __init__(self, seed=88403651, grid_size=8, rounds=10):
        self.seed       = seed
        self.N_small    = grid_size*grid_size + 1      
        self.N_large    = 1123996597
        self.rounds     = rounds

        # build the 10 round keys
        k = seed
        self.round_keys = []
        for _ in range(self.rounds):
            k = (k * seed) % self.N_small
            self.round_keys.append(k)

    def _reverse_feistel(self, state):
        """
        reverse the 10-round mixing network to recover the original 4 words
        """
        s0, s1, s2, s3 = state
        for r in reversed(range(self.rounds)):
            k = self.round_keys[r]
            w2 = s0
            w1 = s3 ^ ((w2*k + k) % self.N_large)
            w0 = s1 ^ ((w1*k + k) % self.N_large)
            w3 = ((s2 - w0*w1*w2) * inverse(w0*w2, self.N_large)) % self.N_large
            s0, s1, s2, s3 = w0, w1, w2, w3
        return [s0, s1, s2, s3]

    def _words_to_bytes(self, words):
        # convert four 32-bit words into 16 big-endian bytes
        out = []
        for w in words:
            out += [(w >> shift) & 0xFF for shift in (24,16,8,0)]
        return out

    def _expand_key(self, key16):
        # turn 16 bytes → 32-byte ChaCha20 key via (b*seed) % 65
        def map_byte(b): return (b * self.seed) % self.N_small
        return bytes(key16 + [map_byte(b) for b in key16])

    def decrypt(self, state_words, nonce, ct_hex):
        # reverse the 10 Feistel rounds
        original = self._reverse_feistel(state_words)

        # extract 16-byte key
        key16    = self._words_to_bytes(original)

        # 3. expand to 32-byte key
        full_key = self._expand_key(key16)

        cipher = ChaCha20.new(key=full_key, nonce=nonce)
        return cipher.decrypt(bytes.fromhex(ct_hex))


v63   = 915433456                
v64_0 = 392537330              

nonce = v63.to_bytes(4, 'little') + v64_0.to_bytes(4, 'little')

v49 = [0x7BEABC84, 0x18418440, 0xDBCBC705]
v50 = [0x9185733C62E8F422, 0x4DCD4172D214DB83]
v51 = 200                  

ct = (
    v49[0].to_bytes(4, 'little') +
    v49[1].to_bytes(4, 'little') +
    v49[2].to_bytes(4, 'little') +
    v50[0].to_bytes(8, 'little') +
    v50[1].to_bytes(8, 'little') +
    v51.to_bytes(2, 'little')
)
ct_hex = ct.hex()


final_state = [342066253, 686880041, 828922036, 509868183]

finisher = FeistelChaCha20Decryptor()
pt = finisher.decrypt(final_state, nonce, ct_hex)
print(pt)
```
![chall-sc](https://hackmd.io/_uploads/HkphqM1_el.png)
 
