mirror of
https://github.com/msikma/pokesprite.git
synced 2026-03-21 17:54:23 -05:00
438 lines
14 KiB
PHP
Executable File
438 lines
14 KiB
PHP
Executable File
#!/usr/bin/env php
|
|
<?php
|
|
|
|
// PokéSprite
|
|
// ----------
|
|
// This simple script generates an image sprite containing small icons of
|
|
// all Pokémon in the National Pokédex (along with several other types of
|
|
// icons), and the pertaining SCSS and JS files. It was built for people
|
|
// working on websites or online applications related to Pokémon. The main
|
|
// Pokémon icons are arranged sequentially, and a growing packer algorithm
|
|
// is used to arrange other images inside the sprite.
|
|
//
|
|
// Aside from the regular box sprites (which have been sourced from
|
|
// Pokémon X/Y), this package includes unofficial custom designed shiny
|
|
// versions of those sprites.
|
|
//
|
|
// The data for the Pokémon icons is taken from the `./data/pkmn.json` file.
|
|
// The other icon sets have their data generated based on the directory
|
|
// structure and file names.
|
|
//
|
|
// ---------------------------------------------------------------------------
|
|
//
|
|
// The MIT License (MIT)
|
|
//
|
|
// (C) 2014-2016 Michiel Sikma <michiel@sikma.org> 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();
|
|
}
|
|
|
|
// Retrieve icons from the set.
|
|
if ($include_icon_sets) {
|
|
$icon_stack->parse_icons_data_file($dir_data.$file_icon_data, $icon_range);
|
|
$icon_data = $icon_stack->get_icon_data();
|
|
|
|
// Check if the data is there.
|
|
$has_data = $icon_stack->has_icon_data();
|
|
$has_imgs = $icon_stack->has_icon_images();
|
|
|
|
if (!$has_data) {
|
|
print(I18n::lf('no_icons_data', array($file_icon_data, $dir_data)));
|
|
die();
|
|
}
|
|
if (!$has_imgs) {
|
|
print(I18n::l('no_icon_images'));
|
|
die();
|
|
}
|
|
}
|
|
|
|
// 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;
|