#!/usr/bin/env php and PokéSprite contributors // // For a full list of contributors, view the project commit history. // // 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. // // --------------------------------------------------------------------------- // // All files in the `icons` directory are included in this package for reasons // of convenience and are (C) 1995-2014 Nintendo/Creatures Inc./GAME FREAK Inc. namespace PkSpr; error_reporting(0); require_once 'includes/templateformatter.php'; require_once 'includes/terminalformatter.php'; require_once 'includes/settings.php'; require_once 'includes/scaffolding.php'; require_once 'includes/i18n.php'; require_once 'includes/usage.php'; require_once 'includes/iconstack.php'; require_once 'includes/functions.php'; require_once 'includes/iconoverview.php'; require_once 'includes/iconstyler.php'; require_once 'includes/iconjs.php'; require_once 'includes/iconsprite.php'; // 1. Parsing of command line arguments and basic program feedback // --------------------------------------------------------------------------- // Load the default settings. Settings::load_settings_file('includes/defaults.php'); // Parse the user's command line arguments and determine whether the user // is in need of seeing the usage guide. $usage = new Usage(); $cl_settings = $usage->get_user_settings(); // Check the current revision number for user feedback and for // inclusion in the output files. $revision = trim(shell_exec('git rev-list HEAD --count 2> /dev/null')); $revision = $revision ? 'r'.$revision : '[unknown]'; $cl_settings['revision'] = $revision; // Retrieve the version number from the pkg-info.json file. $pkg_info = json_decode(file_get_contents(Settings::get('resources_dir').'pkg-info.json'), true); $version = $pkg_info['version']; $cl_settings['version'] = $version; // Merge the user's command line arguments into the settings. Settings::load_settings($cl_settings); if ($usage->needs_version) { // Display version and exit. $usage->load_tpl_file( Settings::get('resources_dir'). Settings::get('version_tpl') ); $usage->display_version(); exit; } if ($usage->needs_usage) { // Display usage and exit. $usage->load_tpl_file( Settings::get('resources_dir'). Settings::get('usage_tpl') ); $usage->display_usage(); exit; } // Export the settings variables so that we can use them here. $vars = array_keys(Settings::$settings); foreach ($vars as $var) { $$var = Settings::get($var); } // Set the default lines. I18n::set_language($i18n_language); I18n::set_default_lines(); $termfrm = new TerminalFormatter(); I18n::add_output_filter(array('PkSpr\TerminalFormatter', 'format')); // Check if we have GD. if (!function_exists('imagecreatefrompng')) { print(I18n::l('gd_error')); die(); } // Print basic program info. print(I18n::lf('info', array( $title_str, $version, $revision, $website_txt, $copyright_gf ))); $generated_on = I18n::lf('generated_on', array($script_date)); // Check if we're running via anything other than the command line. if (php_sapi_name() != 'cli') { print(I18n::l('no_cli')); } // Check if pngcrush exists, in case we need it. if ($generate_optimized === true) { if (!is_file($pngcrush_path)) { print(I18n::lf('pngcrush_missing', array($pngcrush_path))); $generate_optimized = false; } } // Check if the output directory exist and create them if necessary. if (!is_dir($dir_output)) { print(I18n::l('dir_create')); if (!mkdir($dir_output, $dir_mode, true)) { print(I18n::l('dir_error')); die(); } print(I18n::l('dir_success')); } // Print a quick overview of what we'll be doing. print(I18n::l('tasks_overview')); if ($include_pkmn) { print(I18n::lf('task_pkmn', array( $include_pkmn ? 'true' : 'false', $include_pkmn_nonshiny ? 'true' : 'false', $include_pkmn_shiny ? 'true' : 'false', $include_pkmn_forms ? 'true' : 'false', $include_pkmn_right, $pkmn_language ))); } if ($include_icon_sets) { print(I18n::l('task_icon_sets')); } if ($generate_optimized) { print(I18n::l('task_optimize')); } print(I18n::l('task_html')); if ($generate_markdown) { print(I18n::l('task_markdown')); } print(I18n::l('task_scss')); print(I18n::l('task_js')); // 2. Pokémon data parsing and icon stack generation // --------------------------------------------------------------------------- $icon_stack = new IconStack(); // Retrieve Pokémon name and idx information from the data file. if ($include_pkmn) { $icon_stack->parse_data_file($dir_data.$file_pkmn_data, $pkmn_range); $pkmn_data = $icon_stack->get_pkmn_data(); // Check if the data is there. $has_data = $icon_stack->has_pkmn_data(); $has_imgs = $icon_stack->has_pkmn_images(); if (!$has_data) { print(I18n::lf('no_data', array($file_pkmn_data, $dir_data))); die(); } if (!$has_imgs) { print(I18n::l('no_images')); die(); } } else { // If we aren't including Pokémon, keep an empty array // to avoid iteration warnings. $pkmn_data = array(); } // Generate and return the entire Pokémon icon stack. $pkmn_icons = $icon_stack->get_pkmn_icon_stack(); $pkmn_std_icons = $icon_stack->get_pkmn_std_icons(); // Get the total size of the Pokémon icons. We'll position the rest of the // images using the same width, and an arbitrary height. $pkmn_sect_fit = $icon_stack->get_pkmn_icon_stack_size(); // We'll keep images other than Pokémon icons inside of a separate array. // Position them using a binary tree packing algorithm. $etc_icons = $icon_stack->get_etc_icon_stack(); $etc_sets = $icon_stack->get_etc_icon_sets(); $etc_sect_fit = $icon_stack->get_etc_icon_stack_size(); // Full sprite attributes $stack_size = $icon_stack->get_combined_stack_size(); $sprite_width = $stack_size['w']; $sprite_height = $stack_size['h']; // Now we'll generate the sprite sheet. // About how much memory will this use? $mb_estimate = format_bytes($sprite_width * $sprite_height * 32); print(I18n::lf('sprite_stats', array( $sprite_width, $sprite_height, count($etc_icons) + count($pkmn_icons), $mb_estimate, ))); // 3. Generation of the icon sprite // --------------------------------------------------------------------------- $sprite_sections = array( 'pkmn' => $pkmn_sect_fit['h'], 'etc' => $etc_sect_fit['h'], ); $icon_sprite = new IconSprite( $sprite_width, $sprite_height, $sprite_sections, $debug_img_inclusion ); // Pass on data on which icons are the standard variants. $icon_sprite->set_pkmn_std_icons($pkmn_std_icons); // Array of both the Pokémon icons and the other icons. $all_icons = $icon_stack->get_all_icons(); // For the sprite image itself, iterate over all icons and add them. $added = 0; $total = count($all_icons); for ($a = 0; $a < count($all_icons); ++$a) { // IconSprite returns true if successful, false if the image // could not be used somehow. $icon = $all_icons[$a]; if ($icon_sprite->add($icon)) { $added += 1; } } print(I18n::lf('icons_added', array($added))); // Send some feedback in case we couldn't add all icons. if (($added - $total) > 0) { if ($debug_img_inclusion) { print(I18n::lf('icons_skipped', array($added - $total))); } else { print(I18n::lf('icons_skipped_hint', array($added - $total))); } } print(I18n::l('sprite_ready')); print(I18n::lf('sprite_saving', array( $generate_optimized ? $dir_output.$img_output_tmp : $dir_output.$img_output, ))); // Save the initial result to output directory. if (file_exists($dir_output.$img_output_tmp)) { unlink($dir_output.$img_output_tmp); } $icon_sprite->output($dir_output.$img_output_tmp, $generate_optimized !== true); $icon_sprite->destroy(); // Use pngcrush to minimize the image. if ($generate_optimized === true) { print(I18n::l('pngcrush_start')); if (file_exists($dir_output.$img_output)) { unlink($dir_output.$img_output); } $crush_cmd = $pngcrush_path.' -l 9 -q -text b author "Pokémon Sprite Generator v'.$version.' r'.$revision.'" -text b copyright "'.$copyright_gf.'" '.$dir_output.$img_output_tmp.' '.$dir_output.$img_output; exec($crush_cmd); if (file_exists($dir_output.$img_output)) { // Seems like pngcrush was successful. Let's review the results. $size_before = filesize($dir_output.$img_output_tmp); $size_after = filesize($dir_output.$img_output); print(I18n::lf('pngcrush_success', array( format_bytes($size_before), format_bytes($size_after), ))); // Delete the unoptimized image. print(I18n::l('sprite_del_old')); unlink($dir_output.$img_output_tmp); } else { // We couldn't use pngcrush for some reason. print(I18n::l('pngcrush_error')); exit; } } else { // If we're not using pngcrush, just rename it to the final filename. rename($dir_output.$img_output_tmp, $dir_output.$img_output); } // 4. Generation of the HTML, SCSS and JS files pertaining to the sprites // --------------------------------------------------------------------------- // We're going to be making a map of positioning values, styling information // as well as an HTML overview of every icon. $icon_overview = new IconOverview(); $icon_styler = new IconStyler(); $icon_js = new IconJS(); // Create a tree structure of our icon sets based on their subtype information. // This will be given to the SCSS, JS and overview generators. $icon_tree = $icon_stack->get_icon_type_tree(); $set_sizes = $icon_stack->get_set_sizes(); $icon_overview->set_icon_list($all_icons); $icon_styler->set_icon_list($all_icons); $icon_styler->set_icon_sizes($set_sizes); $icon_js->set_sprite_sections($sprite_sections); $icon_js->set_icon_tree($icon_tree); $icon_js->set_icon_sizes($set_sizes); // For the generation of the HTML, SCSS and JS files, a number of // base variables are used. $base_vars = array( 'title_str' => $title_str, 'revision' => $revision, 'version' => $version, 'website_txt' => $website_txt, 'copyright_str' => $copyright_str, 'copyright_gf' => $copyright_gf, 'copyright_contrib_notice' => $copyright_contrib_notice, 'generated_on' => $generated_on, 'css_base_selector' => $css_base_selector, ); // Generate the HTML overview of all icons we've just added. print(I18n::l('html_generating')); $icon_overview->register_vars($base_vars); $icon_overview->register_tpl($resources_dir.$html_tpl); $overview = $icon_overview->get_overview('html'); if (file_exists($dir_output.$html_output)) { unlink($dir_output.$html_output); } file_put_contents($dir_output.$html_output, $overview); // If requested, generate a Markdown overview too. if ($generate_markdown) { print(I18n::l('markdown_generating')); $overview_md = $icon_overview->get_overview('markdown'); if (file_exists($dir_output.$md_output)) { unlink($dir_output.$md_output); } file_put_contents($dir_output.$md_output, $overview_md); } // Generate a page about the build too. $icon_overview->register_tpl($resources_dir.$html_build_tpl); $overview = $icon_overview->get_build_overview('html'); if (file_exists($dir_output.$html_build_output)) { unlink($dir_output.$html_build_output); } file_put_contents($dir_output.$html_build_output, $overview); // With the image and the overview done, let's generate the SCSS. print(I18n::l('scss_generating')); $icon_styler->register_vars($base_vars); $icon_styler->register_tpl($resources_dir.$scss_tpl); $scss = $icon_styler->get_scss(); if (file_exists($dir_output.$scss_output)) { unlink($dir_output.$scss_output); } file_put_contents($dir_output.$scss_output, $scss); // Finally, the JS. print(I18n::l('js_generating')); $icon_js->register_vars($base_vars); $icon_js->register_tpl($resources_dir.$js_tpl); $js = $icon_js->get_js(); if (file_exists($dir_output.$js_output)) { unlink($dir_output.$js_output); } file_put_contents($dir_output.$js_output, $js); // All done! print(I18n::l('all_done')); exit;