@echo off
setlocal
rem ==================================================================
rem  CriCri BR - servidor brasileiro de Among Us no PC
rem  (Steam, Epic Games e itch.io)
rem
rem  O que este arquivo faz (e so isso):
rem    [1] Instalar: guarda uma copia do seu regionInfo.json, poe o
rem        servidor CriCri BR SEM apagar os outros e deixa ele escolhido.
rem    [2] Remover: tira so o CriCri BR e volta a regiao escolhida para uma oficial.
rem  Nao baixa nada da internet e nao precisa de administrador.
rem  Pasta do jogo: AppData\LocalLow\Innersloth\Among Us
rem
rem  Sem o menu:  instalar-cricri-br.cmd instalar
rem               instalar-cricri-br.cmd remover
rem
rem  Daqui para baixo: o cmd so chama o PowerShell do proprio Windows
rem  com a parte escrita no fim deste arquivo (depois da marca POWERSHELL).
rem ==================================================================
title CriCri BR - servidor no Among Us
set "CRICRI_ARQUIVO=%~f0"
set "CRICRI_ACAO=%~1"
"%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe" -NoLogo -NoProfile -Command "$t = [IO.File]::ReadAllText($env:CRICRI_ARQUIVO, [Text.Encoding]::UTF8); $i = $t.IndexOf('####' + ' POWERSHELL ' + '####'); & ([ScriptBlock]::Create($t.Substring($i))); exit $global:CricriSaida"
set "CRICRI_SAIDA=%ERRORLEVEL%"
echo.
if not defined CRICRI_SEM_PAUSA pause
exit /b %CRICRI_SAIDA%

#### POWERSHELL ####
# A partir daqui é PowerShell (o .cmd lê este pedaço do próprio arquivo). Nada aqui baixa coisa da internet.
# Modelo: src\CricriBr.Contas\Recursos\instalar-cricri-br.cmd do CRICRI BR; o site troca os 4 valores abaixo pela UrlPublica.
$ErrorActionPreference = 'Stop'
$global:CricriSaida = 1

# ---- O servidor (o site preenche a partir do endereço público) ----
$Nome     = 'CriCri BR'
$Esquema  = 'https'
$Endereco = 'cricribr.com.br'
$Porta    = 443

# Nomes que versões antigas do site usaram para a MESMA região ("CRICRI BR"): instalar troca pelo nome de agora, remover tira.
$ReNossa = '^(?i)(' + [regex]::Escape($Nome) + '|cricri[ -]?br)$'

$utf8 = New-Object System.Text.UTF8Encoding($false)
function Diga([string]$texto, [string]$cor = 'Gray') { Write-Host $texto -ForegroundColor $cor }
function Termina([int]$codigo) { $global:CricriSaida = $codigo; throw 'CRICRI_FIM' }
function EhNossa($n) { return ([string]$n) -match $ReNossa }

# Texto de uma região no MESMO formato que o jogo grava (o "$type" tem que vir primeiro).
function TextoDaRegiao([string]$n, [string]$ping, [string]$ip, [int]$p, [int]$traducao, [string]$servidor) {
    $n = $n.Replace('\', '\\').Replace('"', '\"')
    return '{"$type":"StaticHttpRegionInfo, Assembly-CSharp","Name":"' + $n + '","PingServer":"' + $ping +
        '","Servers":[{"Name":"' + $servidor + '","Ip":"' + $ip + '","Port":' + $p +
        ',"UseDtls":false,"Players":0,"ConnectionFailures":0}],"TargetServer":null,"TranslateName":' + $traducao + '}'
}
$nossa = TextoDaRegiao $Nome $Endereco "$Esquema`://$Endereco" $Porta 1003 'http-1'
$oficiais = @(
    (TextoDaRegiao 'North America' 'matchmaker.among.us'    'https://matchmaker.among.us'    443 289 'Http-1'),
    (TextoDaRegiao 'Europe'        'matchmaker-eu.among.us' 'https://matchmaker-eu.among.us' 443 290 'Http-1'),
    (TextoDaRegiao 'Asia'          'matchmaker-as.among.us' 'https://matchmaker-as.among.us' 443 291 'Http-1'))

# Início e fim (inclusive) do objeto {...} da PRIMEIRA região nossa a partir de $desde, respeitando aspas. $null se não achar.
function LimitesDaNossa([string]$texto, [int]$desde = 0) {
    $re = New-Object System.Text.RegularExpressions.Regex('"Name"\s*:\s*"((?:[^"\\]|\\.)*)"')
    foreach ($m in $re.Matches($texto, $desde)) {
        if (-not (EhNossa ($m.Groups[1].Value.Replace('\"', '"').Replace('\\', '\')))) { continue }
        # sobe até o "{" que abre o objeto da região (o "Name" da região vem antes de "Servers")
        $inicio = $texto.LastIndexOf('{', $m.Index)
        if ($inicio -lt 0) { return $null }
        $nivel = 0; $emAspas = $false
        for ($i = $inicio; $i -lt $texto.Length; $i++) {
            $c = $texto[$i]
            if ($emAspas) { if ($c -eq '\') { $i++ } elseif ($c -eq '"') { $emAspas = $false }; continue }
            if ($c -eq '"') { $emAspas = $true }
            elseif ($c -eq '{') { $nivel++ }
            elseif ($c -eq '}') { $nivel--; if ($nivel -eq 0) { return @($inicio, $i) } }
        }
        return $null
    }
    return $null
}
# Tira o objeto [ini..fim] da lista, junto com a vírgula vizinha.
function TiraObjeto([string]$texto, $lim) {
    $ini = $lim[0]; $fim = $lim[1]
    if ($texto.Substring(0, $ini).TrimEnd().EndsWith(',')) { $ini = $texto.LastIndexOf(',', $ini) }
    elseif ($texto.Substring($fim + 1).TrimStart().StartsWith(',')) { $fim = $texto.IndexOf(',', $fim + 1) }
    return $texto.Remove($ini, $fim - $ini + 1)
}
function Compacto($obj) { return ($obj | ConvertTo-Json -Depth 10 -Compress) }
function IndicesNossos($dados) {
    $r = @($dados.Regions); $lista = @()
    for ($i = 0; $i -lt $r.Count; $i++) { if (EhNossa $r[$i].Name) { $lista += $i } }
    return , $lista
}
function OficiaisNaFrente($dados) {
    $r = @($dados.Regions)
    return ($r.Count -ge 3) -and ($r[0].Name -eq 'North America') -and ($r[1].Name -eq 'Europe') -and ($r[2].Name -eq 'Asia')
}
function TrocaSelecionada([string]$texto, [int]$indice) {
    $re = New-Object System.Text.RegularExpressions.Regex('"CurrentRegionIdx"\s*:\s*-?\d+')
    if ($re.IsMatch($texto)) { return $re.Replace($texto, '"CurrentRegionIdx":' + $indice, 1) }
    $chave = $texto.IndexOf('{')   # arquivo sem a chave: põe logo no começo do objeto
    return $texto.Insert($chave + 1, '"CurrentRegionIdx":' + $indice + ',')
}
function Menu {
    Diga ''
    Diga '  [1] Instalar o CriCri BR' 'White'
    Diga '  [2] Remover o CriCri BR' 'White'
    Diga '  (Para sair, feche esta janela.)' 'DarkGray'
    Diga ''
    for ($tentativa = 0; $tentativa -lt 5; $tentativa++) {
        $r = Read-Host '  Digite 1 ou 2 e aperte Enter'
        if ($null -eq $r) { return $null }
        $r = $r.Trim()
        if ($r -eq '1') { return 'instalar' }
        if ($r -eq '2') { return 'remover' }
        Diga '  Digite só 1 ou 2.' 'Yellow'
    }
    return $null
}

$codificacaoAntes = $null
try {
    # acentos certos também quando a saída vai para arquivo (e volta como estava no fim)
    try { $codificacaoAntes = [Console]::OutputEncoding; [Console]::OutputEncoding = $utf8 } catch { $codificacaoAntes = $null }
    Diga ''
    Diga '  CriCri BR - servidor brasileiro de Among Us' 'White'
    Diga '  -------------------------------------------' 'DarkGray'
    Diga '  + Adiciona o servidor ao seu jogo.' 'Gray'
    Diga '  + Troque de servidor a qualquer hora: no jogo, em Região, ao criar ou procurar sala.' 'Gray'
    Diga "  Servidor: $Esquema`://$Endereco (porta $Porta). Não baixa nada e não precisa de administrador." 'DarkGray'

    $acao = ([string]$env:CRICRI_ACAO).Trim().ToLowerInvariant()
    if ($acao -eq '1') { $acao = 'instalar' } elseif ($acao -eq '2') { $acao = 'remover' }
    if ($acao -ne 'instalar' -and $acao -ne 'remover') { $acao = Menu }
    if (-not $acao) { Diga ''; Diga '  Nada foi feito.' 'Yellow'; Termina 5 }
    $remover = ($acao -eq 'remover')
    Diga ''

    # ---- 1. Onde o jogo guarda as regiões (LocalLow fica ao lado do LocalAppData) ----
    $pasta = Join-Path (Split-Path $env:LOCALAPPDATA) 'LocalLow\Innersloth\Among Us'
    if (-not (Test-Path -LiteralPath $pasta)) { $pasta = Join-Path $env:USERPROFILE 'AppData\LocalLow\Innersloth\Among Us' }
    $arquivo = Join-Path $pasta 'regionInfo.json'
    $versaoDaLoja = Test-Path -LiteralPath (Join-Path $env:LOCALAPPDATA 'Packages\Innersloth.AmongUs_fw5x688tam7rm')

    if (-not (Test-Path -LiteralPath $pasta)) {
        if ($versaoDaLoja) {
            Diga '  Seu Among Us é o da Microsoft Store / app Xbox.' 'Yellow'
            Diga '  Essa versão não aceita servidor de fora. Jogue pela Steam, Epic ou pelo celular.' 'Yellow'
        } else {
            Diga '  Não achei o Among Us neste PC.' 'Yellow'
            Diga '  Abra o jogo uma vez, feche e rode este arquivo de novo.' 'Yellow'
        }
        Termina 3
    }

    # ---- 2. O jogo tem que estar fechado (ele só lê o arquivo quando abre, e regrava ao trocar de região) ----
    if (-not $env:CRICRI_TESTE_SEM_JOGO) {
        $aberto = { @(Get-Process -Name 'Among Us' -ErrorAction SilentlyContinue).Count -gt 0 }
        if (& $aberto) {
            Diga '  Feche o Among Us para continuar... (esperando)' 'Yellow'
            $limite = (Get-Date).AddMinutes(3)
            while (& $aberto) {
                if ((Get-Date) -gt $limite) { Diga '  O Among Us continua aberto. Feche e rode de novo.' 'Red'; Termina 2 }
                Start-Sleep -Milliseconds 700
            }
            Start-Sleep -Seconds 2   # tempo do jogo terminar de gravar ao fechar
        }
    }

    # ---- 3. Lê o que já existe ----
    $antes = $null; $dadosAntes = $null
    if (Test-Path -LiteralPath $arquivo) {
        $antes = [IO.File]::ReadAllText($arquivo, $utf8)   # tira o BOM, se tiver
        try { $dadosAntes = $antes | ConvertFrom-Json } catch { $dadosAntes = $null }
        if ($dadosAntes -and -not ($dadosAntes.PSObject.Properties.Name -contains 'Regions')) { $dadosAntes = $null }
    }

    # ---- 4. Monta o arquivo novo ----
    $novo = $null; $selecionou = $false; $aviso = $null; $voltouPara = $null
    if ($remover) {
        $nossos = IndicesNossos $dadosAntes
        if (-not $dadosAntes -or $nossos.Count -eq 0) { Diga '  O CriCri BR não está no seu jogo. Nada mudou.' 'Green'; Termina 0 }
        $novo = $antes
        for ($k = 0; $k -lt 50; $k++) { $lim = LimitesDaNossa $novo; if (-not $lim) { break }; $novo = TiraObjeto $novo $lim }
        $sel = [int]$dadosAntes.CurrentRegionIdx
        if ($nossos -contains $sel) { $novoSel = 0 } else { $novoSel = $sel - @($nossos | Where-Object { $_ -lt $sel }).Count }
        if ($novoSel -ne $sel) { $novo = TrocaSelecionada $novo $novoSel }
        if ($nossos -contains $sel) {
            # O nome como o jogo em português mostra na lista de Região.
            $voltouPara = [string]@(($novo | ConvertFrom-Json).Regions)[0].Name
            $emPortugues = @{ 'North America' = 'América do Norte'; 'Europe' = 'Europa'; 'Asia' = 'Ásia' }
            if ($emPortugues.ContainsKey($voltouPara)) { $voltouPara = $emPortugues[$voltouPara] }
        }
    }
    elseif (-not $dadosAntes) {
        # Sem arquivo (ou arquivo estragado): começa do jeito que o jogo grava, com as 3 oficiais + a nossa.
        if ($antes) { $aviso = 'O arquivo antigo estava estragado: fiz um novo (a cópia está guardada).' }
        $novo = '{"CurrentRegionIdx":3,"Regions":[' + ($oficiais -join ',') + ',' + $nossa + ']}'
        $selecionou = $true
    }
    else {
        $lim = LimitesDaNossa $antes
        if ($lim) {
            # Já tinha: troca só o objeto dela, no mesmo lugar (o endereço ou o nome podem ter mudado), e tira repetidas.
            $novo = $antes.Remove($lim[0], $lim[1] - $lim[0] + 1).Insert($lim[0], $nossa)
            $depoisDela = $lim[0] + $nossa.Length
            for ($k = 0; $k -lt 50; $k++) { $outra = LimitesDaNossa $novo $depoisDela; if (-not $outra) { break }; $novo = TiraObjeto $novo $outra }
        } else {
            # Não tinha: acrescenta no fim da lista "Regions" (o fim do arquivo é "]}").
            $m = [regex]::Match($antes, '\]\s*\}\s*$')
            if (-not $m.Success) { Diga '  O arquivo está num formato que eu não conheço. Nada mudou.' 'Red'; Termina 4 }
            $corpo = $antes.Substring(0, $m.Index).TrimEnd()
            $separador = if ($corpo.EndsWith('[')) { '' } else { ',' }
            $novo = $corpo + $separador + $nossa + ']}'
        }
        # Deixa a nossa escolhida, mas só se as 3 oficiais estão na frente (assim a posição no arquivo é a do jogo).
        $dadosTmp = $novo | ConvertFrom-Json
        if (OficiaisNaFrente $dadosTmp) { $novo = TrocaSelecionada $novo (IndicesNossos $dadosTmp)[0]; $selecionou = $true }
        else { $aviso = "No jogo, toque em Jogar e escolha $Nome em Região." }
    }

    # ---- 5. Confere ANTES de gravar: JSON válido, nossa região certa e as outras intactas ----
    $depois = $novo | ConvertFrom-Json
    $outrasAntes = @(); if ($dadosAntes) { $outrasAntes = @(@($dadosAntes.Regions) | Where-Object { -not (EhNossa $_.Name) } | ForEach-Object { Compacto $_ }) }
    $outrasDepois = @(@($depois.Regions) | Where-Object { -not (EhNossa $_.Name) } | ForEach-Object { Compacto $_ })
    $ok = $true
    if ($remover) {
        $total = @($depois.Regions).Count
        $ok = ((IndicesNossos $depois).Count -eq 0) -and ($outrasDepois.Count -eq $outrasAntes.Count) -and
              ([int]$depois.CurrentRegionIdx -ge 0) -and ([int]$depois.CurrentRegionIdx -lt [Math]::Max(1, $total))
    } else {
        $nossos = IndicesNossos $depois
        $i = if ($nossos.Count -eq 1) { $nossos[0] } else { -1 }
        $r = if ($i -ge 0) { @($depois.Regions)[$i] } else { $null }
        $s = if ($r) { @($r.Servers)[0] } else { $null }
        $ok = ($i -ge 0) -and ($r.Name -ceq $Nome) -and $s -and ($s.Ip -eq "$Esquema`://$Endereco") -and ([int]$s.Port -eq $Porta)
        if ($dadosAntes) { $ok = $ok -and ($outrasDepois.Count -eq $outrasAntes.Count) } else { $ok = $ok -and (@($depois.Regions).Count -ge 4) }
        if ($selecionou) { $ok = $ok -and ([int]$depois.CurrentRegionIdx -eq $i) }
        elseif ($dadosAntes) { $ok = $ok -and ([int]$depois.CurrentRegionIdx -eq [int]$dadosAntes.CurrentRegionIdx) }
    }
    if ($ok -and $dadosAntes) { for ($k = 0; $k -lt $outrasAntes.Count; $k++) { if ($outrasAntes[$k] -ne $outrasDepois[$k]) { $ok = $false } } }
    if (-not $ok) { Diga '  A conferência falhou: não mexi em nada. Use o outro jeito, na página do site.' 'Red'; Termina 4 }

    if ($null -ne $antes -and $novo -ceq $antes) {
        Diga '  O CriCri BR já estava no seu jogo. Nada mudou.' 'Green'
        Diga '  Abra o Among Us, toque em Jogar e escolha CriCri BR em Região.' 'White'
        Termina 0
    }

    # ---- 6. Cópia de segurança + grava (UTF-8 sem BOM, troca de uma vez só) ----
    $copia = $null
    if (Test-Path -LiteralPath $arquivo) {
        $copia = Join-Path $pasta ('regionInfo.antes-do-cricri-' + (Get-Date -Format 'yyyy-MM-dd_HH-mm-ss') + '.json')
        Copy-Item -LiteralPath $arquivo -Destination $copia -Force
    }
    $temp = Join-Path $pasta 'regionInfo.json.cricri-novo'
    [IO.File]::WriteAllText($temp, $novo, $utf8)
    $soLeitura = $false
    try {
        if (Test-Path -LiteralPath $arquivo) {
            $info = Get-Item -LiteralPath $arquivo
            $soLeitura = $info.IsReadOnly   # tem gente que trava o arquivo; destrava, grava e trava de novo
            if ($soLeitura) { $info.IsReadOnly = $false }
            [IO.File]::Replace($temp, $arquivo, [NullString]::Value)
        } else {
            [IO.File]::Move($temp, $arquivo)
        }
    } finally {
        if ($soLeitura -and (Test-Path -LiteralPath $arquivo)) { (Get-Item -LiteralPath $arquivo).IsReadOnly = $true }
        if (Test-Path -LiteralPath $temp) { Remove-Item -LiteralPath $temp -Force }
    }

    if ($remover) {
        Diga '  Pronto! O CriCri BR saiu do seu jogo.' 'Green'
        if ($voltouPara) { Diga "  A região escolhida voltou para $voltouPara." 'White' }
    } else {
        Diga '  Pronto! O CriCri BR está no seu jogo.' 'Green'
        if ($selecionou) { Diga '  Abra o Among Us e toque em Jogar: a Região já vem no CriCri BR.' 'White' }
        Diga '  Para tirar depois: abra este arquivo de novo e escolha 2.' 'Gray'
    }
    if ($aviso) { Diga "  $aviso" 'Yellow' }
    if ($copia) { Diga ('  Cópia do arquivo antigo: ' + (Split-Path $copia -Leaf)) 'DarkGray' }
    if ($versaoDaLoja) { Diga '  (O Among Us da Microsoft Store / app Xbox não usa este arquivo.)' 'DarkGray' }
    Termina 0
}
catch {
    if ($_.Exception.Message -ne 'CRICRI_FIM') {
        Diga ''
        Diga ('  Algo deu errado: ' + $_.Exception.Message) 'Red'
        Diga '  Nada foi apagado. Use o outro jeito, na página do site.' 'Yellow'
        $global:CricriSaida = 1
    }
}
finally {
    if ($codificacaoAntes) { try { [Console]::OutputEncoding = $codificacaoAntes } catch { } }
}
