diff --git a/scripts/class.FactorioMod.php b/scripts/class.FactorioMod.php new file mode 100644 index 0000000..aed31a9 --- /dev/null +++ b/scripts/class.FactorioMod.php @@ -0,0 +1,331 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit) + */ +class FactorioMod { + + /** + * @var int INIT_TYPE_PATH - Mod is initialized from a zip file. + */ + const INIT_TYPE_PATH = 1; + + /** + * @var int INIT_TYPE_NAME - Mod is initialized from a mod name. + */ + const INIT_TYPE_NAME = 3; + + + /** + * @var object|null $info - Mod information from Zip file. + */ + private $info = null; + + /** + * @var object|null $api - Mod information from Factorio API. + */ + private $api = null; + + /** + * @var object|null $mod_settings - Mod settings enabled/disabled from json in mods folder. + */ + private $mod_settings = null; + + /** + * @var string|null $mod_folder - Path to the mods folder. + */ + private $mod_folder = null; + + /** + * Reads all Mods as FactorioMod objects to an array. + * @param string $modFolder Path to the mods folder. + * @return array Array of FactorioMod objects. + */ + static function readModFolder(string $modFolder) : array { + + $mods = []; + + $dh = opendir($modFolder); // open the directory handle + + while ( false !== ($entry = readdir($dh)) ) { // read directory entries + + if(preg_match("/\.zip$/ui", $entry)) { // if the file is a zip file + + $mods[] = new FactorioMod( + $modFolder. "/". $entry, + FactorioMod::INIT_TYPE_PATH + ); + + } + + } + + closedir($dh); // close the directory handle + + return $mods; + + } + + /** + * Reads mod information from info.json file in mod Zip file. + * @param string $zipPath Path to the mod Zip file. + * @return object|null Mod information from info.json file. + */ + private function readInfo(string $zipPath) : ?object { + + if(!file_exists($zipPath)) { // file does not exist? + return null; + } + + $za = new \ZipArchive(); + $res = $za->open($zipPath); // open the zip file + + if($res !== true) { // open the zip file failed? + return null; + } + + $c = null; + for( $i = 0; $i < $za->numFiles; $i++ ){ // Iterate over all files in the zip file + + $name = $za->getNameIndex( $i ); // get the name of current file + + if(basename($name) == "info.json") { // if the file is info.json + $c = $za->getFromIndex($i); // get the content of info.json + $i = $za->numFiles; // break the loop by condition + break; // break the loop by force (double tap) + } + + } + + $za->close(); // close the zip file + + return json_decode( $c, false ); // return decoded json object or null on failure + + } + + /** + * Sends a GET request to Factorio API to get mod information. + * @param string $name Mod name to get information from Factorio API. + * @return object|null Mod information from Factorio API. + */ + private function callApi(string $name="") : ?object { + + if($name === "") { // no name given? + if($this->info === null) { // also no path to module for info.json? + return null; + } + $name = $this->info->name; // get name from info.json + } + + $api = "https://mods.factorio.com/api/mods/" . $name; + + $curl = curl_init(); + + curl_setopt($curl, CURLOPT_URL, $api); + curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); + + $response = curl_exec($curl); + $cri = curl_getinfo($curl); + + curl_close($curl); + + if($cri["http_code"] != 200) { // request failed? + return null; + } + + if(trim($response) < 1) { // response body empty? + return null; + } + + $r = json_decode($response, false); // decode json object or null on failure + + return $r; + } + + /** + * Constructor. + * @param string $init Path to the mod Zip file or mod name. + * @param int $init_type Initialization type (FactorioMod::INIT_TYPE_PATH or FactorioMod::INIT_TYPE_NAME). + * @param string $mod_folder Path to the mods folder. + */ + public function __construct(string $init, int $init_type=self::INIT_TYPE_PATH, string $mod_folder="") { + + if($init_type === self::INIT_TYPE_PATH) { + + $this->info = $this->readInfo($init); + if($this->info !== null) { + $this->mod_folder = dirname($init); + $this->callApi(); + } + + } + + if($init_type === self::INIT_TYPE_NAME) { + + $this->mod_folder = $mod_folder; + $this->callApi($init); + + // TODO: look if mod is installed.. + + } + + } + + /** + * Returns last release version information of the mod. + * @return object|null Last release version information of the mod. + */ + public function getLastVersionInfo() : ?object { + + $target = count($this->api->releases) - 1; + + return $this->api->releases[ $target ]; + + } + + /** + * Checks if the latest release mod version is newer than the installed version. + * @return bool True if the latest release mod version is newer, false otherwise. + */ + public function isUpdateable() : bool { + + $lv = new \NAE\Factorio\FactorioVersion( + $this->getLastVersionInfo()->version + ); + + $cv = new \NAE\Factorio\FactorioVersion( + $this->info->version + ); + + return $lv->isNewer($cv); + + } + + /** + * Deletes the mod from the mods folder file. + * @return bool True if the mod was deleted, false otherwise. + */ + private function delete() : bool { + // TODO: implement + return false; + } + + /** + * Downloads the latest release mod version to the mods folder. + * @return bool True if the mod was downloaded, false otherwise. + */ + private function download() : bool { + // TODO: implement + return false; + } + + /** + * Uninstalls the mod from the mods folder and the settings.json file. + * @return bool True if the mod was uninstalled, false otherwise. + */ + public function uninstall() : bool { + // TODO: implement + return false; + } + + /** + * Installs the mod in the mods folder and adds it to the settings.json file as enabled. + * @return bool True if the mod was installed, false otherwise. + */ + public function install() : bool { + // TODO: implement + return false; + } + + /** + * Updates the mod to the latest release mod version. + * @return bool True if the mod was updated, false otherwise. + */ + public function update() : bool { + // TODO: implement + return false; + } + + /** + * Loads the mod settings for all mods (enabled/disabled) from the settings.json file. + * @return bool True if the mod settings have been loaded successfully, false otherwise. + */ + private function loadModSettings() : bool { + // TODO: implement + return false; + } + + /** + * Returns the mod settings for all mods (enabled/disabled) from settings.json. + * @return object|null Mod settings for all mods. + */ + public function getModSettings() :?object { + return $this->mod_settings; + } + + /** + * Saves the changes in the mod settings for all mods (enabled/disabled) to the settings.json file. + * @return bool True if the mod settings have been saved successfully, false otherwise. + */ + private function saveModSettings() : bool { + + $j = json_encode($this->mod_settings); + // TODO: implement + + return false; + } + + /** + * Sets the mod settings for all mods (enabled/disabled) in the settings.json file. + * @param object $mod_settings Mod settings for all mods. + * @return FactorioMod The instance of FactorioMod for chaining. + */ + public function setModSettings(object $mod_settings) : FactorioMod { + + $this->mod_settings = $mod_settings; + + return $this; + + } + + /** + * Checks if this mod is enabled in the settings.json. + * @return bool True if this mod is enabled, false otherwise. + */ + public function isEnabled() : bool { + // TODO: implement + return false; + } + + /** + * Checks if this mod is disabled in the settings.json. + * @return bool True if this mod is disabled, false otherwise. + */ + public function isDisabled() : bool { + // TODO: implement + return false; + } + + /** + * Sets this mod to enabled in the settings.json + */ + public function enable() { + // TODO: implement + } + + /** + * Sets this mod to disabled in the settings.json + */ + public function disable() { + // TODO: implement + } + +}