FontAwesome 5 Pro et Free JSON Cheatsheet

La cheatsheet de FontAwesome 5 n’étant pas à jour, j’ai créé un gist pour référencer les icones en version Gratuite ou Pro.

FontAwesome 5 Pro Json CheatSheet

FontAwesome 5 Free Json CheatSheet

Si vous souhaitez générer un fichier différent, voici également un json bien plus complet que vous pourrez parser vous même :

FontAwesome 5 Full Json Data

Version utilisée : 5.0.12

Séparateurs SVG programmables

Je publie aujourd’hui un helper PHP qui permet de dessiner des séparateurs en SVG très facilement avec quelques paramètres.

svg separators

 

ExpanDrive ou comment gérer tous ses clouds facilement !

J’étais bloqué récemment avec Hubic, le cloud Français d’OVH. Son offre à 10 TO pour 50€/ans est de loin la plus compétitive à l’heure actuelle. Mais ce service de cloud n’est pas des plus pratiques. La ou dropbox, amazon, google drive vous offrent la possibilité de choisir avec précision les dossiers que vous souhaitez synchroniser, Hubic ne vous permet de choisir uniquement dans le premier niveau de sous dossiers du répertoire Hubic principal.

Pour pallier à cela, j’ai créé des dossiers vraiment spécifiques, en essayant d’avoir le maximum de répertoires accessibles à la racine d’hubic !

Donc au lieu d’avoir une architecture comme celle ci :

Hubic
    |-PROJETS
        |- 2018
            |-CLIENT 1

J’ai donc opté pour une hierarchie comme celle ci :

Hubic
    |-PROJETS - 2017 - CLIENT 1

Mais on se retrouve tout de même assez vite bloqué, certains dossiers possèdent des milliers de fichiers, et la synchronisation peut être extrêmement lente et fastidieuse. Lorsque vous avez besoin d’un fichier spécifique ou d’un groupe de fichier dans un dossier très lourd pas encore synchronisé, vous n’avez plus qu’à vous tourner vers l’espace en ligne hubic.com.

La encore un autre problème survient, l’interface web n’est pas capable de créer des zip complets lorsque le dossier est trop volumineux !

Il me restait donc deux solutions, changer de Cloud, mais avec mes 1 TO déjà hébergés, il est difficile de revenir en arrière, ou trouver un logiciel qui permette de naviguer sur hubic, mieux que Hubic le propose lui même.

J’ai d’abord tenté le cloud d’Amazon, mais leur logiciel de synchronisation est également peu fonctionnel, beaucoup de bugs. Dropbox et Google Drive sont performant et pratiques mais leur offrent de stockage sont beaucoup plus onéreuses…

Que faire dans ce cas ?

C’est la ou la solution parfait m’est apparu. Je suis rapidement tombé sur ExpanDrive qui permet de monter des clouds comme disque externe et d’y naviguer librement sans avoir a synchroniser tout un dossier ! C’est à ma connaissance le seul logiciel qui permette de monter un cloud Hubic dans votre poste de travail et de manière hyper simple.

La procédure est la suivante :

Tout d’abord il vous faut choisir les clouds que vous possédez et que vous souhaitez monter. La liste est énorme, tous les clouds principaux sont présents. J’ai pour cet exemple choisi de connecter mes comptes Dropbox, Amazon, Google Drive et bien sur Hubic.

 

expandrive1

Une fois vos comptes connectés, ces clouds appaitront dans la liste principale. Vous pouvez depuis cette page accèder à vos dossier, éditer les noms et lettres de lecteur ou supprimer des comptes.expandrive2Et comme par magie, vos clouds se retrouvent ensuite dans votre poste de travail ! Vous avez même accès à l’indicateur d’utilisation des disques. C’est parfait !

expandrive3

Pour Hubic, c’est la fonctionnalité manquante du logiciel officiel ! Vous pourrez alors vous promener dans vos dossiers librement et ne copier que les fichiers dont vous avez besoin. La copie s’effectue comme dans un dossier classique ! La vitesse de copie peut varier en fonction du service de clouds et de votre réseau, mais le gain de temps de ne pas avoir à passer par l’interface web peu fonctionnelle est un énorme avantage !

ExpanDrive est compatible Windows et Mac et les mises à jours sont fréquentes. Comme tout bon logiciel, il n’est pas gratuit, mais pas d’abonnement mensuel ! pour 50$ le logiciel complet est à vous à vie… Un bon investissement si vous êtes comme moi un grand utilisateur de clouds !

https://www.expandrive.com/

 

PHP Script To Copy Files Recursively With Options

, ,

This script will help you copy files and folders from one location to another, recursively.
For each file or folder you can have copy options.

Here is an example conf file :

:: GitHub File ::
[/etc/php/cacert.pem] keep_existing = 1 [/etc/nginx/nginx.conf] keep_existing = 0 backup = 1 [/etc/nginx/sites-available/default] keep_existing = 0 backup = 1 [/etc/nginx] keep_existing = 1 [/home/default] keep_existing = 1

In this example, the file data/etc/nginx/sites-available/default will be copied to /etc/nginx/sites-available/default, and if the file already exists it will be backuped and overwritten.
In the same way, all the files inside data/etc/nginx will be copied to /etc/nginx, but existing file will be ignored.

How To

Place folders and files that you want to copy in data/
Edit filecopier-config.ini to configure which files and folders you want to copy, backup and overwrite

Command Lines :

php filecopier.php process_all [options]

This command will execute every entries in the filecopier-config.ini file

Config file options :

option values default description
keep_existing 1/0 0 keep file if already exists, do not overwrite it
backup 1/0 0 create a backup of the file if exists, then copy the new file
debug 1/0 0 write the copy in a test/ folder
php confeditor.php process filepath [options]

This command will copy the file located in data/filepath to filepath

Cmd options :

option equivalent values default
-ke keep_existing 1/0 0
-b backup 1/0 0
-d debug 1/0 0

PHP Script

:: GitHub File ::
<?php if(count($argv) >= 2 ) { if($argv[1] == 'process' && count($argv) >= 4 ) { process($argv[2], $argv[3]); } else if($argv[1] == 'process-all' ) { $config = parse_ini('confeditor-config.ini',false,false); //print_r($config); //exit(); foreach($config as $k=>$v) { $AddEdit = $k; $original = $v['original']; process($k, $original, $v); } } } function process( $editFileName, $originalFile, $config = null ) { global $argv; // gettings extra params $keepComments = getSetting( $config, $argv, 'keep_comments', '-kc', false, false); $keepEmptyLines = getSetting( $config, $argv, 'keep_empty_lines', '-kel', false, false); $sort = getSetting( $config, $argv, 'sort', '-sort', false, false); $debug = getSetting( $config, $argv, 'debug', '-d', false, false); $separator = getSetting( $config, $argv, 'separator', '-sep', true, '='); $commentRegex = getSetting( $config, $argv, 'comment_regex', '-cr', true, '-^(;|#)-'); $separator = str_replace('"', '', $separator); $commentRegex = str_replace('"', '',$commentRegex); //exit( '$keepComments '.$keepComments. ' $keepEmptyLines '.$keepEmptyLines. ' $sort '.$sort.' $separator '.$separator.' $commentRegex '.$commentRegex.' $debug '.$debug); $editFile = './data/'.$editFileName; if(!file_exists($editFile)) die( "editFile does not exist ".$editFile); if(!file_exists($originalFile)) die( "originalFile does not exist ".$originalFile); $to = $debug ? $originalFile.'-new.ini' : $originalFile; backup_file($originalFile); $ini = parse_ini($originalFile, $keepComments, $keepEmptyLines, $separator, $commentRegex ); $iniNewValues = parse_ini($editFile, false, false); $iniEdited = edit_ini($ini, $iniNewValues, $sort); save_ini($to, $iniEdited); //print_r($config); print_r($ini); print_r($iniNewValues); print_r($iniEdited); //$ini2 = parse_ini($file2, true); //print_r($ini2); } function getSetting( $config, $argv, $varName, $argName, $keepNextArg, $default) { if(!empty($config) && isset($config[$varName])) return $config[$varName]; if(!empty($argv) && !empty($argName) ) { if(in_array($argName, $argv) ) return $keepNextArg ? $argv[ array_search($argName, $argv) + 1 ] : true; } return $default; } function backup_file ($file) { //echo $file.'.bak'; if(!copy( $file, $file.'.bak')) //if(!file_put_contents( $file.'.bak', file_get_contents( $file))) exit("la création du backup a échoué"); } function edit_ini ( $ini, $iniNewValues, $sort = false ) { $ini = array_replace_recursive( $ini, $iniNewValues ); $globals = []; $sections = []; foreach( $ini as $k=>$v) { if(is_array($v)) //section $sections[$k] = $v; else $globals[$k] = $v; } if($sort) { ksort($globals); //print_r($globals); foreach($sections as $k => $v) { ksort($v); $sections[$k] = $v; //print_r($sections[$k]); } } return $globals + $sections; } function save_ini ( $filepath, $ini, $save = true ) { $str = ''; foreach( $ini as $k=>$v) { if(is_array($v)) //section { $str .= "[$k]".PHP_EOL; $str .= save_ini( '', $v, false); }else { if( preg_match('#^comment#', $k )) $str .= $v.PHP_EOL; else if( preg_match('#^empty#', $k )) $str .= PHP_EOL; else $str .= "$k = $v".PHP_EOL; } } if($save) file_put_contents($filepath, $str ); else return $str; } function parse_ini ( $filepath, $keepComments = true, $keepEmptyLines = true, $separator = '=', $commentRegex = '-^(;|#)-' ) { $ini = file( $filepath ); if ( count( $ini ) == 0 ) { return array(); } $sections = array(); $values = array(); $globals = array(); $result = array(); $i = 0; $j = 0; foreach( $ini as $line ){ $line = trim( $line ); // Sections if ( !empty($line) && $line{0} == '[' ) { $sections[] = substr( $line, 1, -1 ); $i++; continue; } // Key-value pair if ( $line == '' ) { if($keepEmptyLines) { $key = 'empty'.$j++; $value = $line; //echo $value; } else continue; }else if ( preg_match($commentRegex, $line) ) { if($keepComments ) { $key = 'comment'.$j++; $value = $line; //echo $value }else continue; }else if( preg_match( '#'.$separator.'#', $line)){ list( $key, $value ) = explode( $separator, $line, 2 ); } $key = trim( $key ); $value = trim( $value ); if ( $i == 0 ) { // Array values if ( substr( $line, -1, 2 ) == '[]' ) { $globals[ $key ][] = $value; } else { $globals[ $key ] = $value; } } else { // Array values if ( substr( $line, -1, 2 ) == '[]' ) { $values[ $i - 1 ][ $key ][] = $value; } else { $values[ $i - 1 ][ $key ] = $value; } } } for( $j=0; $j<$i; $j++ ) { $result[ $sections[ $j ] ] = $values[ $j ]; } return $globals + $result; } ?>

The full repository is here : https://github.com/anthonykozak/iniTools

How To Edit ini & conf Files With Shell Scripts

,

I wrote some scripts to help editing ini files and conf files while reinstalling my servers.

The script is able to process multiple files at once and will add or edit the parameters you want automatically.

the configuration file looks like this :

:: GitHub File ::
[_www.conf] original = /etc/php/5.6/fpm/pool.d/www.conf keep_comments = 0 keep_empty_lines = 0 sort = 1 [_sysctl.conf] original = /etc/sysctl.conf keep_comments = 1 keep_empty_lines = 1 [_php-fpm.conf] original = /etc/php/5.6/fpm/php-fpm.conf keep_comments = 0 keep_empty_lines = 0 [_php.ini] original = /etc/php/5.6/fpm/php.ini keep_comments = 1 keep_empty_lines = 1

As you can see for each file I want to edit, there is a small file containing only the lines I need to be edited or added. For exxample the _php-fpm.conf file looks like this :

:: GitHub File ::
[global] error_log = /var/log/php/php-fpm.log emergency_restart_threshold = 10 emergency_restart_interval = 1m process_control_timeout = 20s

 

Command Lines :

php confeditor.php process_all [options]

This command will execute every entries in the confeditor-config.ini file

file options :

option values default description
keep_comments 1/0 0 keep comment lines in the target file
keep_empty_lines 1/0 0 keep empty lines in target file
sort 1/0 0 sort ini keys in each section
debug 1/0 0 do not write in files but create a ‘-new’ file instead
separator string ‘=’ separator for key & values
comment_regex regex ‘-^(;|#)-‘ what defines a comment
php confeditor.php process sample-file target-file [options]

This command will merge the target-file with the lines inside the data/sample-file

Cmd options :

option equivalent values default
-kc keep_comments 1/0 0
-kel keep_empty_lines 1/0 0
-sort sort 1/0 0
-d debug 1/0 0
-sep separator string ‘=’
-cr comment_regex string ‘-^(;|#)-‘

The main repository is here : https://github.com/anthonykozak/iniTools

Upgrade EasyPHP’s php version without having to pay for warehouse

,

So you like Easyphp and you just want to upgrade you PHP version?

Easyphp now comes with a website called Warehouse, allowing you to download modules and components but the access is not free. Of course there is plenty of reasons you would pay for such service but if you like me just want to upgrade your PHP version follow those steps :

1. Download the windows PHP version you want on the official site : http://windows.php.net/download/, for example the VC11 x86 Thread Safe (2016-Jun-22 21:49:59) version. (I did not tested with the a x64 version).

2. Unzip the package to your EasyPHP « php » folder. (Create a folder with your version name) for example : C:\EasyPHP-DevServer-14.1VC9\binaries\php\php-5.6\

3. Copy the file « easyphp.php » from php_runningversion/easyphp.php to php-5.6/easyphp.php and edit it.

4. Just indicate a version number, a dir name and a new date greater that the previous one

<?php
$phpversion = array();
$phpversion = array(
"status"    => "0",
"dirname"    => "php5623x160719154425",
"name"         => "PHP",
"version"     => "5.6.23",
"date"         => "2016-07-20 15:44:24",
"notes"     => "",
);
?>

5. Go to you EasyPHP admin page and click on the link to change your php version (http://127.0.0.1/home/index.php?page=php-page&display=changephpversion). Your version should appear on the list, select it.

6. Restart EasyPhp your version should be upgraded. Now be sure to download the appropriate extensions for your version on the official site : http://windows.php.net/downloads/pecl/releases/

Geo Master est sur Greenlight !

,

Je vous présente mon dernier jeu nommé « Geo Master », jeu de géographie en 3D. Ce jeu offre plusieurs modes d’apprentissage ludiques. Vous avez la possibilité d’explorer le globe, de tester vos connaissances sans challenge puis de vous confronter au monde entier dans des parties chronométrées sur la planète entière ou sur le continent de votre choix !

Le mode UNESCO vous offre également la possibilité de découvrir les monuments et lieux classés au patrimoine mondial.

Le mode « Faits incroyables » vous donne des détails amusants et surprenants sur la planète Terre.
Enfin, un mode multijoueur vous permet d’affronter d’autres joueurs en ligne ou en local en temps réel !

Geo Master vient d’être publié sur les plateformes mobiles, et j’ai besoin de votre aide pour être publié par Steam ! Si vous avez un compte Steam je compte sur vous pour voter ! 🙂

greenlight_vote

 

[TUTORIAL] Unity3D – Signing and Packaging your game for the Mac AppStore and Outside !

, ,

I spent some times to understand the way to sign and package an .app generated from Unity3D for the Mac AppStore or for outside of it. The workflow is quite the same, you will need to type a few lines of code in order to generate the proper files.

The few differences are that you don’t need the « entitlements »  file and the certifactes are different.

If you want to publish your game outside the store, from the Apple Member Center, you will need to generate the « DEVELOPER ID » application and installer certificates. Then install them by drag and dropping them into you keychain access.

For the Mac AppStore, you have to generate the « MAC APP STORE » production application and installer certifcates.

I made a script in order to automate the signing and packaging process. Here it is on GitHub :

:: GitHub File ::
#!/usr/bin/perl use File::Copy; use File::Find; use File::Path; use Cwd; # Exoa SignAndPackage script v1.3 # Author : Anthony Kozak :: exoa.fr # Place this script in the same folder as the generated .app file from Unity # YOU WOULD NEED IN THIS DIRECTOY: # - a filled Info.plist file # - a PlayerIcon.icns file # - a filled entitlements.entitlements file # - a UnityPlayerIcon.png file # YOU CAN CHECK YOUR INSTALLED CERTIFICATES NAMES USING # security find-identity -p codesigning logit("Hello !"); my $app_found = found_app_indir(); my $appName = ask("What's the .app name in this directory ?", $app_found); my $appPath = getcwd . "/".$appName.".app"; my $appType = ask("Is this app for the MacAppStore or External ?","MacAppStore"); my $appPathSigned = getcwd . "/".$appType."/".$appName.".app"; my $packagePath = getcwd . "/".$appType."/".$appName.".pkg"; #my $profile = ask("What's the provision profile name to use in this directory ?","profile.provisionprofile"); my $doCodeSigning = ask("Sign the app ?", "true"); my $doCreatePackage = ask("Generate package ?","true"); my $copyInfopList = ask("Copy Info.plist from this directory inside the .app ?","true"); my $copyIcons = ask("Copy PlayerIcon.icns from this directory inside the .app ?","false"); my $copyIcon = ask("Copy UnityPlayerIcon.png from this directory inside the .app ?","true"); my $srcAssetPath = "/"; my $certificateApplication = ask("What's the application certificate name ?", $appType eq "MacAppStore" ? "3rd Party Mac Developer Application:" : "Developer ID Application:"); my $certificateInstaller = ""; if($doCreatePackage eq "true") { $certificateInstaller = ask("What's the installer certificate name ?", $appType eq "MacAppStore" ? "3rd Party Mac Developer Installer:" : "Developer ID Installer:"); } my $entitlementsFileName = ""; if($appType eq "MacAppStore") { $entitlementsFileName = ask("What's the .entitlements file name in this directory ?", "entitlements.entitlements"); $entitlementsFileName = "--entitlements \"".$entitlementsFileName."\""; } logit("*** Starting Process - Building at '$appPath' ***"); # src and dest are temp variables. Just ignore them... 🙂 my $src = ""; my $dest = ""; # Removing previous dir rmtree $appType; # Creating target dir mkdir $appType, 0755; # this copies your own /Info.plist to the generated game if($copyInfopList eq "true") { $plist = getcwd . $srcAssetPath . "Info.plist"; $dest = $appPath . "/Contents/Info.plist"; print ("\n*** Copying " . getShort($plist). " to " . getShort($dest)); copy($plist, $dest) or die "File can not be copied: " . $plist; } # this copies PlayerIcon.icns to your generated game replacing the original app icon by your own if($copyIcons eq "true") { $icons = getcwd . $srcAssetPath . "PlayerIcon.icns"; $dest = $appPath . "/Contents/Resources/PlayerIcon.icns"; print ("\n*** Copying " . $icons . " to " . $dest); copy($icons, $dest) or die "File can not be copied: " . $icons; } # this copies /UnityPlayerIcon.png to your generated game replacing the original Unity Player Icon by your own if($copyIcon eq "true") { $playericon = getcwd . $srcAssetPath . "UnityPlayerIcon.png"; $dest = $appPath . "/Contents/Resources/UnityPlayerIcon.png"; print ("\n*** Copying " . getShort($playericon) . " to " . getShort($dest)); copy($playericon, $dest) or die "File can not be copied: " . $playericon; } # this copies $profile to your generated game #$src = getcwd . $srcAssetPath . $profile; #$dest = $appPath . "/Contents/embedded.provisionprofile"; #print ("\n*** Copying " . getShort($src) . " to " . getShort($dest)); #copy($src, $dest) or die "File can not be copied: " . $src; # this copies appPath to appPathSigned and use it print ("\n*** Copying " . getShort($appPath) . " to " . getShort($appPathSigned)); system("cp -r \"".$appPath."\" \"".$appPathSigned."\""); ## Chmod and remove unecessary files system("/bin/chmod -R a+rwx \"$appPathSigned\""); system("find \"$appPathSigned\" -name \*.meta -exec rm -r \"{}\" \\;"); system("/usr/sbin/chown -RH \"cestdesbullshit1:staff\" \"$appPathSigned\""); system("/bin/chmod -RH u+w,go-w,a+rX \"$appPathSigned\""); my $CodesignEnvironment = $ENV{'CODESIGN_ALLOCATE'}; $ENV{'CODESIGN_ALLOCATE'}="/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/codesign_allocate"; # Auto code signing if ($doCodeSigning eq "true") { recursiveCodesign("$appPathSigned/Contents/Frameworks"); recursiveCodesign("$appPathSigned/Contents/Plugins"); recursiveCodesign("$appPathSigned/Contents/MacOS"); logit ("*** Start signing"); system("/usr/bin/codesign --force --timestamp=none --sign \"" . $certificateApplication . "\" ".$entitlementsFileName." \"" . $appPathSigned . "\""); logit ("*** Verify signing"); system("codesign --verify --verbose \"" . $appPathSigned . "\""); } # Auto creating a package file? if ($doCreatePackage eq "true") { logit("*** Start packaging"); system("productbuild --component \"" . $appPathSigned . "\" /Applications --sign \"". $certificateInstaller . "\" --product \"$appPathSigned/Contents/Info.plist\" \"" . $packagePath . "\""); } $ENV{'CODESIGN_ALLOCATE'}=$CodesignEnvironment; logit("*** ALL DONE ! ***"); sub ask{ my $text = shift; my $default = shift; logit($text . " [default: ".$default."]"); my $answer = <STDIN>; chomp $answer; return ($answer eq "") ? $default : $answer; } sub logit{ my $text = shift; print("\n".$text."\n"); } sub recursiveCodesign { my $dirName = shift; print("\n*** Recursive Codesigning ".getShort($dirName)."\n"); opendir my($dh), $dirName or return; my @files = readdir($dh); closedir $dh; foreach my $currentFile (@files) { next if $currentFile =~ /^\.{1,2}$/; if ( lc($currentFile) =~ /.bundle$/ or lc($currentFile) =~ /.dylib$/ or lc($currentFile) =~ /.a$/ or lc($currentFile) =~ /.so$/ or lc($currentFile) =~ /.lib$/ or (-f "$dirName/$currentFile" && $currentFile =~ /^[^.]*$/ && `file "$dirName/$currentFile"` =~ /Mach-O/) ) { print("\tCodesigning ".getShort($currentFile)."\n"); system("/usr/bin/codesign --force --timestamp=none --sign \"".$certificateApplication."\" \"$dirName/$currentFile\""); } if (-d "$dirName/$currentFile") { recursiveCodesign("$dirName/$currentFile"); } } } sub found_app_indir() { opendir(my $dh, '.') || die "cant open dir"; my @list_found = grep { /.app$/ } readdir($dh); my $app_found = @list_found[0]; chop($app_found); chop($app_found);chop($app_found); chop($app_found); return $app_found; } sub getShort() { my $subject = shift; my $search = getcwd . "/"; my $replace = ""; my $pos = index($subject, $search); while($pos > -1) { substr($subject, $pos, length($search), $replace); $pos = index($subject, $search, $pos + length($replace)); } return $subject; }

1) After installing your certifcates, place this script in the same directory as your .app generated by Unity3D

2) Add the optional files in the same directory such as  Info.plist, PlayerIcon.icns,  entitlements.entitlements,  UnityPlayerIcon.png. They will be overridden inside the .app

3) Open a terminal and launch the script ./SignAndPackage.pl

4) Answer the configuration questions and let the script do the job !

 

Note : You can check that your certificates are installed using this command line :

security find-identity -p codesigning

You don’t need to fill the full name of the certifcates when requested. The default values can be good enough. For example if your certificate is called « 3rd Party Mac Developer Installer: MY DEVELOPER NAME (68684684) », you can just enter « 3rd Party Mac Developer Installer ».

 Edit: Version 1.1 now available

Edit: Version 1.3 removed the embedded profile that was crashing the Application Loader

Unity3D – Tips when building your game with an Editor script

, ,

Here are some basic tricks when you need to build your game by code.

First you can launch a build process with a single line of code, for example :

[MenuItem("Exoa/Build/Android")]
public static void PerformBuildAndroid()
{
	string fullPath = Application.dataPath.Replace("/Assets", "/") + "_BUILDS/android/build.apk";
	// Build Game
	BuildPipeline.BuildPlayer(allScenes, fullPath, BuildTarget.StandaloneWindows, BuildOptions.None);
}

For Android

Then you can install your game on the connected Android device like this :

[MenuItem("Exoa/Build/Install build on Android")]
public static void InstallAndroid()
{
	// Run the game (Process class from System.Diagnostics).
	string fullPath = Application.dataPath.Replace("/Assets", "/") + "_BUILDS/android/build.apk"; 
	proc = new System.Diagnostics.Process();
	proc.StartInfo.FileName = "D:/SDK/android/sdk/platform-tools/adb.exe"; // replace with your adb exe path
	proc.StartInfo.Arguments = "-d install -r " + fullPath;
	proc.Start();
}

Then you can launch the application on the android device like this :

[MenuItem("Exoa/Build/Launch on Android")]
public static void LaunchAndroid()
{
	// Run the game (Process class from System.Diagnostics).
	string fullPath = Application.dataPath.Replace("/Assets", "/") + "_BUILDS/android/build.apk";
	string appid = PlayerSettings.bundleIdentifier;
	proc = new System.Diagnostics.Process();
	proc.StartInfo.FileName = "D:/SDK/android/sdk/platform-tools/adb.exe"; // replace with your adb exe path
	proc.StartInfo.Arguments = "shell am start -n " + appid + "/com.unity3d.player.UnityPlayerNativeActivity"; 
	proc.Start();
}

For Desktop

For desktop releases, you can automatically launch the build with the Process() class :

[MenuItem("Exoa/Build/Run Exe %&r")]
public static void RunGame()
{
	string fullPath = Application.dataPath.Replace("/Assets", "/") + "_BUILDS/win/build.exe";

	// Run the game (Process class from System.Diagnostics).
	Process proc = new Process();
	proc.StartInfo.FileName = fullPath;
	proc.Start();
}

Opening build folder

You can finally automatically open the build folder like Unity do :

[MenuItem("Exoa/Build/Open Build Folder")]
public static void OpenBuildFolder()
{
	string fullPath = Application.dataPath.Replace("/Assets", "/") + "_BUILDS/";
	fullPath = fullPath.Replace("/","\\");
	Debug.Log(fullPath);
	proc = new System.Diagnostics.Process();
	proc.StartInfo.FileName = "explorer.exe";
	proc.StartInfo.Arguments = fullPath;
	proc.Start();
}

PHP Mobile icons & splashcreens generator

When you publish a game to the Google play store, the AppStore and so on, you face the boring wall of icons, splashcreens and screenshots sizes. You need to generate a lots of differents images for each platform you target and that can be a huge waste of time.
I didn’t found any service letting you generate the exact sizes you need, so I made my own.

Here is MobileIconGenerator hosted service.

And here is the source code on GitHub.

thumb