All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.ocraft.s2client.api.controller.ExecutableParser Maven / Gradle / Ivy

package com.github.ocraft.s2client.api.controller;

/*-
 * #%L
 * ocraft-s2client-api
 * %%
 * Copyright (C) 2017 - 2018 Ocraft Project
 * %%
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 * #L%
 */

import com.github.ocraft.s2client.protocol.GameVersion;
import com.github.ocraft.s2client.protocol.Versions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Stream;

import static com.github.ocraft.s2client.api.OcraftApiConfig.*;
import static com.github.ocraft.s2client.protocol.Constants.nothing;
import static com.github.ocraft.s2client.protocol.Errors.required;
import static com.github.ocraft.s2client.protocol.Preconditions.isSet;
import static java.lang.String.format;
import static java.util.Comparator.reverseOrder;
import static java.util.Optional.ofNullable;

public final class ExecutableParser {

    private static Logger log = LoggerFactory.getLogger(ExecutableParser.class);

    public static final String BUILD_PREFIX = "Base";
    public static final String VERSIONS_DIR = "Versions";
    public static final String EXECUTE_INFO = "ExecuteInfo.txt";
    public static final String X64_SUFFIX = "_x64";
    public static final Path CFG_PATH = Paths.get("Starcraft II", EXECUTE_INFO);
    public static final Path WIN_CFG = Paths.get("Documents").resolve(CFG_PATH);
    public static final Path LINUX_CFG = CFG_PATH;
    public static final Path MAC_CFG = Paths.get("Library", "Application Support", "Blizzard").resolve(CFG_PATH);

    private ExecutableParser() {
        throw new AssertionError("private constructor");
    }

    public static Map loadSettings(
            String customPath, String customDataVersion, String customBaseBuild) {
        try {
            Map executableConfig = new HashMap<>();

            if (customPath == null) {
                executableConfig.put(GAME_EXE_PATH, findExecutablePath().toString());
            } else {
                executableConfig.put(GAME_EXE_PATH, customPath);
            }

            Path executablePath = Paths.get((String) executableConfig.get(GAME_EXE_PATH));

            if (executablePath.getNameCount() < 3) {
                log.error("Invalid path to the executable file: {}", executablePath);
                throw new StarCraft2ControllerException(
                        ControllerError.INVALID_EXECUTABLE,
                        format("Invalid path to the executable file: %s", executablePath));
            }

            if (Files.notExists(executablePath)) {
                String errorMsg = format("File %s does not exist.", executablePath);
                log.error(errorMsg);
                throw new StarCraft2ControllerException(ControllerError.EXECUTABLE_NOT_FOUND, errorMsg);
            }

            Path gamePath = toGameRootPath().apply(executablePath);
            executableConfig.put(
                    GAME_EXE_FILE,
                    executablePath.subpath(gamePath.getNameCount() + 2, executablePath.getNameCount()).toString());


            executableConfig.put(GAME_EXE_ROOT, gamePath.toString());
            String exeFile = (String) executableConfig.get(GAME_EXE_FILE);

            String baseBuild = customBaseBuild;

            if (!isSet(baseBuild)) {
                baseBuild = toNewestBaseBuild(exeFile).apply(gamePath.resolve(VERSIONS_DIR));
            }

            executableConfig.put(GAME_EXE_BUILD, baseBuild);
            executableConfig.put(GAME_EXE_IS_64, exeFile.contains(X64_SUFFIX));

            if (!isSet(customDataVersion)) {
                Optional gameVersion = Versions.versionFor(
                        Integer.parseInt(baseBuild.replaceFirst(BUILD_PREFIX, "")));
                gameVersion.ifPresent(ver -> executableConfig.put(GAME_EXE_DATA_VER, ver.getDataHash()));
            }

            return executableConfig;
        } catch (IllegalArgumentException e) {
            log.error("Invalid argument was provided.", e);
            throw new StarCraft2ControllerException(
                    ControllerError.INVALID_EXECUTABLE, "Invalid argument was provided", e);
        }
    }

    private static Path findExecutablePath() {
        Path executeInfoPath = resolveExecuteInfoPath().filter(Files::exists).orElseThrow(required(EXECUTE_INFO));
        try (Stream lines = Files.lines(executeInfoPath)) {
            return lines.findFirst()
                    .map(splitToKeyValue())
                    .filter(correctProperty())
                    .filter(notEmptyValue())
                    .map(toPropertyValue())
                    .map(Paths::get)
                    .filter(Files::exists)
                    .orElseThrow(required("executable path"));
        } catch (IOException e) {
            log.error("Finding executable path error.", e);
            throw new StarCraft2ControllerException(
                    ControllerError.INVALID_EXECUTABLE, "Finding executable path error.", e);
        }
    }

    private static Optional resolveExecuteInfoPath() {
        String os = System.getProperty("os.name").toLowerCase();
        String userHome = System.getProperty("user.home");
        if (isWindows(os)) {
            return Optional.of(Paths.get(userHome).resolve(WIN_CFG));
        } else if (isUnix(os)) {
            return Optional.of(Paths.get(userHome).resolve(LINUX_CFG));
        } else if (isMac(os)) {
            return Optional.of(Paths.get(userHome).resolve(MAC_CFG));
        } else {
            return Optional.empty();
        }
    }

    private static boolean isWindows(String os) {
        return os.contains("win");
    }

    private static boolean isMac(String os) {
        return os.contains("mac");
    }

    private static boolean isUnix(String os) {
        return os.contains("nix") || os.contains("nux") || os.contains("aix");
    }

    private static Function splitToKeyValue() {
        return property -> property.split("=");
    }

    private static Predicate correctProperty() {
        return property -> property.length == 2;
    }

    private static Predicate notEmptyValue() {
        return property -> property[1] != nothing() && !property[1].isEmpty();
    }

    private static Function toPropertyValue() {
        return property -> property[1].trim();
    }

    private static Function toGameRootPath() {
        if (!isMac(System.getProperty("os.name").toLowerCase())) {
            return exePath -> exePath.resolve(Paths.get("..", "..", "..")).normalize();
        } else {
            return exePath -> exePath.resolve(Paths.get("..", "..", "..", "..", "..", "..")).normalize();
        }
    }

    private static Function toNewestBaseBuild(String exeFile) {
        return versionPath -> {
            try (Stream builds = Files.list(
                    ofNullable(versionPath)
                            .filter(Files::exists)
                            .orElseThrow(required("version directory")))
                    .filter(file -> file.getFileName().toString().startsWith(BUILD_PREFIX))
            ) {
                return builds.min(reverseOrder())
                        .filter(path -> Files.exists(path.resolve(exeFile)))
                        .map(Path::getFileName)
                        .map(Path::toString)
                        .orElseThrow(required("base build"));
            } catch (IOException e) {
                log.error("Newest base build not found.", e);
                throw new StarCraft2ControllerException(
                        ControllerError.INVALID_EXECUTABLE, "Newest base build not found.", e);
            }
        };
    }

    public static boolean findBaseExe(Path rootPath, int baseBuild, String exeFile) {
        // TODO p.picheta to test
        return Files.exists(rootPath.resolve(Paths.get(VERSIONS_DIR, BUILD_PREFIX + baseBuild, exeFile)));
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy