package main
import (
    "encoding/hex"
    "encoding/json"
    "flag"
    "fmt"
    "io/fs"
    "log"
    "os"
    "path/filepath"
    "strings"
    "sync"
)
func fileNameWithoutExtSliceNotation(fileName string) string {
    return fileName[:len(fileName)-len(filepath.Ext(fileName))]
}
func setNotEncrypted(systemJsonFilePath string) {
    content, err := os.ReadFile(systemJsonFilePath)
    if err != nil {
        log.Fatal("Error when opening file: ", err)
    }
    var data map[string]interface{}
    err = json.Unmarshal(content, &data)
    if err != nil {
        log.Fatal("Error during Unmarshal(): ", err)
    }
    data["hasEncryptedImages"] = false
    data["hasEncryptedAudio"] = false
    dataBytes, err := json.Marshal(data)
    if err != nil {
        log.Fatalln("Error setting encrypted to false :(")
    }
    os.WriteFile(systemJsonFilePath, dataBytes, 0644)
}
func main() {
    log.Println("Starting RPGMV Decryptor...")
    path, err := os.Getwd()
    flag.StringVar(&path, "path", path, "Game Directory")
    flag.Parse()
    if err != nil {
        log.Println(err)
    }
    systemJson := filepath.Join(path, "/www/data/System.json")
    log.Printf("Looking for System.json in %s\n", systemJson)
    key, error := getKey(systemJson)
    if error != nil {
        log.Fatalln("Key not found :(")
    }
    log.Printf("Found key! %s", hex.EncodeToString(key))
    filepath.Join(path, "/www")
    walkDir(filepath.Join(path, "/www"), key)
    setNotEncrypted(systemJson)
    os.WriteFile(filepath.Join(path, "/www/Game.rpgproject"), []byte("RPGMV 1.0.0"), 0644)
}
func decryptFile(path string, key []byte) []byte {
    log.Println(path)
    encryptedFile, err := os.ReadFile(path)
    if err != nil {
        log.Fatalf("%s had a fatal error of %s", path, err)
    }
    encryptedFile = encryptedFile[16:]
    ciphertext := encryptedFile[:16]
    plaintext := make([]byte, len(ciphertext))
    for i := range plaintext {
        encryptedFile[i] = ciphertext[i] ^ key[i%len(plaintext)]
    }
    return encryptedFile
}
func decryptAndOutput(path, ext string, key []byte) {
    defer os.Remove(path)
    decryptedFile := decryptFile(path, key)
    newFilename := fmt.Sprintf("%s.%s", fileNameWithoutExtSliceNotation(path), ext)
    os.WriteFile(newFilename, decryptedFile, 0644)
}
var wg sync.WaitGroup
func walkDir(assetPath string, key []byte) {
    filepath.WalkDir(assetPath, func(path string, d fs.DirEntry, err error) error {
        wg.Add(1)
        go func() {
            defer wg.Done()
            if strings.HasSuffix(path, "rpgmvo") {
                decryptAndOutput(path, ".ogg", key)
            }
            if strings.HasSuffix(path, "rpgmvm") {
                decryptAndOutput(path, "m4a", key)
            }
            if strings.HasSuffix(path, "rpgmvp") {
                decryptAndOutput(path, "png", key)
            }
        }()
        return nil
    })
    wg.Wait()
}
func getKey(systemJsonFilePath string) ([]byte, error) {
    content, err := os.ReadFile(systemJsonFilePath)
    if err != nil {
        log.Fatal("Error when opening file: ", err)
    }
    var data map[string]interface{}
    err = json.Unmarshal(content, &data)
    if err != nil {
        log.Fatal("Error during Unmarshal(): ", err)
    }
    key := data["encryptionKey"].(string)
    return hex.DecodeString(key)
}