Advertisement
krom

DAZ-ConvertPoses_G3toG8

Jun 16th, 2017
143
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 18.52 KB | None | 0 0
  1. <?php
  2.  
  3. /*
  4.     Convert Genesis 3 poses to Genesis 8 (female)
  5.  
  6.     author  : mork
  7.     created : 16.06.2017
  8.     version : 1.21
  9.     license : NOMIL NOGOV, besides that, do with it what you want.
  10.     os      : linux - yes
  11.               windows - not tested, needs a binary for unpacking gz and check if a file is compressed
  12.               mac - don't know. could work
  13.  
  14.     dependencies:
  15.         + gunzip
  16.         + file
  17.     challenges:
  18.         + Content downloaded with Connect are in a different folder
  19.         + Connect folders are all lowercase, regular content folders are not
  20.  
  21.     v1.1
  22.         + fixed wrong order of copy/create folder in copyPoseFile
  23.         + removed creating obsolete output folder
  24.         + dunno :)
  25.     v1.2
  26.         + converted to class
  27.     v1.21
  28.         + fixed/added some comments, some cleanup
  29.  
  30.  */
  31.  
  32. set_time_limit(1800);
  33.  
  34. define('LBR', "\n");
  35. define('SEP', DIRECTORY_SEPARATOR);
  36.  
  37.  
  38. class DAZ_ConvertPose  {
  39.  
  40.     private $_path_source;
  41.     private $_path_destination;
  42.     private static $_s_bDebug = false;
  43.  
  44.     private static $_s_dep_gunzip;
  45.     private static $_s_dep_file;
  46.  
  47.     private static $_s_path_tmp = false;
  48.     private $_subfolder;
  49.  
  50.     // if something from this list is within a source path, path is ignored
  51.     // for me, clothing poses were the only thing causing troubles
  52.     private static $_s_blacklist_source = [
  53.         'clothing'
  54.     ];
  55.  
  56.     public function __construct()  {
  57.         $this->_subfolder = '';
  58.     }
  59.  
  60.     // -------------------------------------------------------------------- //
  61.  
  62.     public function setDebug($bool)  {
  63.         self::$_s_bDebug = (bool)$bool;
  64.     }
  65.  
  66.     // -------------------------------------------------------------------- //
  67.  
  68.     public static function s_checkDependencies()  {
  69.         /*----------  We need something to unpack gz files  ----------*/
  70.         exec('gunzip -h', $foo, $status);
  71.         if ($status == 0)  {
  72.             self::$_s_dep_gunzip = 'gunzip -f';
  73.         } else {
  74.             echo "ERROR: missing dependency 'gunzip'. Please install it first.".LBR;
  75.             echo " Example APT    : sudo apt-get install gunzip".LBR;
  76.             echo " Example pacman : sudo pacman -S gunzip".LBR;
  77.             return false;
  78.         }
  79.  
  80.         exec('file --help', $foo, $status);
  81.         if ($status == 0)  {
  82.             self::$_s_dep_file = 'file';
  83.         } else {
  84.             echo "ERROR: missing dependency 'file'. Please install it first.".LBR;
  85.             # no clue which package, should be on every linux distribution
  86.             #echo " Example APT    : sudo apt-get install gunzip".LBR
  87.             #echo " Example pacman : sudo pacman -S gunzip".LBR
  88.             return false;
  89.         }
  90.         return true;
  91.     }
  92.  
  93.     // -------------------------------------------------------------------- //
  94.  
  95.     public function run()  {
  96.  
  97.         /*----------  Scan for Pose folders  ----------*/
  98.         $source_folders = self::_s_collectPoseFolders($this->_path_source);
  99.         if (!$source_folders)  {
  100.             echo "ERROR : No source pose folders found".LBR;
  101.             return false;
  102.         }
  103.         /*----------  Scan for poses  ----------*/
  104.         $poses = self::_s_scanPoseFolders($source_folders);
  105.         if (!$poses)  {
  106.             echo "ERROR : No poses found. Scanned ".sizeof($source_folders)." sources.".LBR;
  107.             return false;
  108.         }
  109.  
  110.         /*----------  Enable debug mode?  ----------*/
  111.         $r = self::_s_askYesNo("Enable debug mode?");
  112.         if ($r == 'y' || $r == 'j')
  113.             $this->setDebug(true);
  114.  
  115.         /*----------  If debug, output all source folders we will copy poses from  ----------*/
  116.         if (self::$_s_bDebug)  {
  117.             echo "Source folders:".LBR;
  118.             foreach($source_folders as $k => $v) echo "  $v".LBR;
  119.         }
  120.  
  121.         /*---------- Init TMP dir ----------*/
  122.         $this->initTmpDir();
  123.  
  124.         /*----------  Ask user if he wants a subfolder  ----------*/
  125.         $r = self::_s_askYesNo("Should I copy everything to a sub-folder in the G8 Pose folder?");
  126.         if ($r == 'y' || $r == 'j')  {
  127.             do {
  128.                 $p = self::_s_readString("Please enter a name for the subfolder");
  129.  
  130.                 $r = self::_s_askYesNo("Is this correct: ".$p);
  131.             } while ($r != 'y' && $r != 'j');
  132.             $this->_subfolder = $p;
  133.         }
  134.  
  135.         /*----------  Copy poses and convert  ----------*/
  136.         $this->_convertAndCopy($poses);
  137.  
  138.         /*----------  Cleanup  ----------*/
  139.         $this->_cleanup();
  140.     }
  141.  
  142.     // -------------------------------------------------------------------- //
  143.  
  144.     public function setPathSource($source_path)  {
  145.         if (!is_dir($source_path)) return false;
  146.         // there should be a 'People' folder in the library
  147.         if (!is_dir($source_path . SEP . 'People')) return false;
  148.  
  149.         $this->_path_source = $source_path;
  150.  
  151.         return true;
  152.     }
  153.  
  154.     // -------------------------------------------------------------------- //
  155.  
  156.     public function setPathDestination($destination_path)  {
  157.         if (!is_dir($destination_path)) return false;
  158.         // if we want to be flexible, it prohibits itself to do additional checks
  159.         // it's virtually impossible to overwrite source content, so...
  160.  
  161.         $this->_path_destination = $destination_path;
  162.  
  163.         return true;
  164.     }
  165.  
  166.     public function initTmpDir()  {
  167.         self::$_s_path_tmp = sys_get_temp_dir() . SEP . 'dazconverttmp';
  168.         self::_s_checkPath(self::$_s_path_tmp);
  169.     }
  170.  
  171.     // -------------------------------------------------------------------- //
  172.  
  173.     /**
  174.      *
  175.      * Cleanup any mess we might have created (temp files)
  176.      *
  177.      */
  178.     private function _cleanup()  {
  179.         if (self::$_s_path_tmp && is_dir(self::$_s_path_tmp))  {
  180.             rmdir(self::$_s_path_tmp);
  181.         }
  182.     }
  183.  
  184.     /**
  185.      *
  186.      * Copy all given pose files to DST_PATH/Genesis 8/Poses/<subfolder-if-any>/SRC_POSE
  187.      *
  188.      */
  189.     private function _convertAndCopy($poses) {
  190.         if (!$poses)           return false;
  191.         if (!is_array($poses)) return false;
  192.  
  193.         $old_folder = '';
  194.         $ignore = 0; // 0 = ask for every file, 1 = ignore all, 2 = ignore current folder
  195.  
  196.         /*----------  loop over all found pose files (DUF or PNG or whatever)  ----------*/
  197.         foreach($poses as $poseIndex => $pose_source)  {
  198.  
  199.             // replace source path with destination path, but keep subfolder structure
  200.             $dst = $this->_path_destination . SEP . str_replace($this->_path_source, '', $pose_source);
  201.             // convert G3 to G8 in destination folder structure
  202.             $dst = str_replace('Genesis 3', 'Genesis 8', $dst);
  203.             // if user provided a subfolder, insert it
  204.             if (strlen($this->_subfolder))  {
  205.                 $dst = str_replace("Poses".SEP, "Poses".SEP.$this->_subfolder.SEP, $dst);
  206.             }
  207.  
  208.             // if user ignored folder, check if we have a new folder
  209.             if (dirname($dst) != $old_folder)  {
  210.                 if ($ignore == 2) $ignore = 0; // if "folder yes", but folder changed, reset ignore to ask user again.
  211.             }
  212.  
  213.             $old_folder = dirname($dst);
  214.  
  215.             // if user is not ignoring, ask him what to do
  216.             if ($ignore == 0)  {
  217.                 echo "Src: ".$pose_source.LBR;
  218.                 echo "Dst: ".$dst.LBR;
  219.                 $r = self::_s_askYesNoExt("Convert this file?", array("y" => "(y)es", "n" => "(n)o", "f" => "(f)older yes", "a" => "(a)ll yes", "c" => "(c)ancel"));
  220.                 switch($r)  {
  221.                     case 'y' :
  222.                     break;
  223.  
  224.                     case 'n' :
  225.                         continue;
  226.  
  227.                     case 'f' :
  228.                         $ignore = 2;
  229.                     break;
  230.  
  231.                     case 'a' :
  232.                         $ignore = 1;
  233.                     break;
  234.  
  235.                     case 'c' : default :
  236.                         return false;
  237.                     break;
  238.                 }
  239.                 echo LBR;
  240.             }
  241.  
  242.  
  243.             /*----------  Make sure target folder exists  ----------*/
  244.             self::_s_checkPath(dirname($dst));
  245.  
  246.             /*----------  Copy file  ----------*/
  247.             if (copy($pose_source, $dst) == false)  {
  248.                 echo "ERROR : Failed to copy file to $dst".LBR;
  249.                 $r = self::_s_askYesNo("Abort?");
  250.                 if ($r == 'y' || $r == 'j') return false;
  251.             }
  252.  
  253.             /*----------  If a DUF file, process  ----------*/
  254.             if (strtolower(self::_s_getFileExtension(basename($dst))) == 'duf')  {
  255.                 self::_s_processPoseFile($dst);
  256.             }
  257.         }
  258.     }
  259.  
  260.     // -------------------------------------------------------------------- //
  261.  
  262.     /**
  263.      *
  264.      * Do a recursive scan on a source pose folder.
  265.      * Basically collects all files within.
  266.      * The provided source folder may be an array of source folders.
  267.      *
  268.      */
  269.     private static function _s_scanPoseFolders($scan_folder)  {
  270.  
  271.         $out = [];
  272.  
  273.         // we accept an array of folder, so if we get one, run this function for each.
  274.         if (is_array($scan_folder))  {
  275.             foreach($scan_folder as $k => $v)  {
  276.                 $ret = self::_s_scanPoseFolders($v);
  277.                 if ($ret) $out = array_merge($out, $ret);
  278.             }
  279.             return $out;
  280.         }
  281.  
  282.         if (!is_dir($scan_folder)) return false; // actually can't happen. only if user deletes it meanwhile.
  283.  
  284.         $files = scandir($scan_folder);
  285.         if (!$files || sizeof($files) == 2) return false;
  286.  
  287.         foreach($files as $k => $file)  {
  288.             if ($file == '.' || $file == '..') continue;
  289.  
  290.             if (is_dir($scan_folder . DIRECTORY_SEPARATOR . $file))  {
  291.                 if(self::$_s_bDebug)
  292.                     echo "Is folder: ". $scan_folder . DIRECTORY_SEPARATOR . $file . LBR;
  293.                 // run this function on pose folder
  294.                 $ret = self::_s_scanPoseFolders($scan_folder . DIRECTORY_SEPARATOR . $file);
  295.                 if ($ret) $out = array_merge($out, $ret);
  296.             } else {
  297.                 // add as valid source file
  298.                 $out[] = $scan_folder . DIRECTORY_SEPARATOR . $file;
  299.  
  300.                 if(self::$_s_bDebug)
  301.                     echo "\t_s_scanPoseFolders | Added: ".$scan_folder . DIRECTORY_SEPARATOR . $file.LBR;
  302.             }
  303.         }
  304.         return $out;
  305.     }
  306.  
  307.     // -------------------------------------------------------------------- //
  308.     /**
  309.      *
  310.      * Find all "Poses" folders within a "Genesis 3 Female" folder.
  311.      * Might need some adjustment for Connect
  312.      *
  313.      */
  314.     private static function _s_collectPoseFolders($dir)  {
  315.         if (!is_dir($dir)) return false;
  316.  
  317.         $out = [];
  318.  
  319.         $f   = scandir($dir);
  320.         if (!$f || sizeof($f) == 2) return false;
  321.  
  322.  
  323.         foreach ($f as $key => $folder_name) {
  324.             if ($folder_name == '.' || $folder_name == '..') continue;
  325.  
  326.             if (!is_dir($dir . DIRECTORY_SEPARATOR . $folder_name)) continue;
  327.  
  328.             // did we find a "poses" folder?
  329.             if (strtolower($folder_name) == 'poses')  {
  330.                 // verify that we are within a genesis 3 subfolder
  331.                 if (strrpos(strtolower($dir), 'genesis 3 female') !== false)  {
  332.                     // blacklist check
  333.                     $bIgnore = false;
  334.                     if (sizeof(self::$_s_blacklist_source))  {
  335.                         foreach (self::$_s_blacklist_source as $key => $value) {
  336.                             if (stripos($dir . DIRECTORY_SEPARATOR . $folder_name, $value) !== false)  {
  337.                                 echo "Ignoring source folder due to blacklist ($value) : " . $dir . DIRECTORY_SEPARATOR . $folder_name . LBR;
  338.                                 $bIgnore = true;
  339.                                 break;
  340.                             }
  341.                         }
  342.                     }
  343.                     // add to list
  344.                     if (!$bIgnore)  {
  345.                         echo "Added source folder : " . $dir . $folder_name .  LBR;
  346.                         $out[] = realpath($dir . DIRECTORY_SEPARATOR . $folder_name);
  347.                     }
  348.                 }
  349.             } else {
  350.                 $b = self::_s_collectPoseFolders($dir . DIRECTORY_SEPARATOR . $folder_name);
  351.                 if ($b) $out = array_merge($out, $b);
  352.             }
  353.         }
  354.         return $out;
  355.     }
  356.  
  357.     /**
  358.      *
  359.      * Read a DUF file, change some values, write back.
  360.      * Uncompresses file to tmp folder, if necessary.
  361.      *
  362.      */
  363.     private static function _s_processPoseFile($file)  {
  364.         /*----------  Does file exist?  ----------*/
  365.         if (!file_exists($file)) return false;
  366.  
  367.         /*----------  Check if the DUF file is compressed  ----------*/
  368.         $fileinfo = exec(self::$_s_dep_file . ' "'.$file.'"');
  369.  
  370.         $src = $file;
  371.  
  372.         if (strpos($fileinfo, 'compressed') !== false && strpos($fileinfo, 'uncompressed') == false)  {
  373.             if(self::$_s_bDebug)
  374.                 echo "\tis compressed: $file".LBR;
  375.  
  376.             $tmp_path = self::$_s_path_tmp . SEP . basename($file).SEP;
  377.  
  378.             self::_s_checkPath($tmp_path);
  379.  
  380.             if(self::$_s_bDebug)
  381.                 echo "\tuncompressing: $file\n\t\t".$tmp_path . SEP . basename($file).LBR;
  382.  
  383.             copy($file, $tmp_path . basename($file).'.gz');
  384.             exec(self::$_s_dep_gunzip .' "'.($tmp_path . basename($file).'.gz').'"');
  385.  
  386.             $src = $tmp_path . basename($file);
  387.  
  388.         } else {
  389.             if(self::$_s_bDebug)
  390.                 echo "\tis uncompressed: $file".LBR;
  391.         }
  392.  
  393.  
  394.         /*----------  Convert Pose  ----------*/
  395.         $data = file_get_contents($src);
  396.         if ($data)  {
  397.             // Shoulder bend left
  398.             // find the values of the lShldrBend.... keys
  399.             if (preg_match("/lShldrBend:\?rotation\/z\/value\"(.*)\"keys\"\s*:\s\[\s*\[(.*)\]\s*\]\n/isU", $data, $match))  {
  400.                $values = explode(",", $match[2]); // separate
  401.                $values[0] = trim($values[0]);     // clean
  402.                $values[1] = trim($values[1]);     // clean
  403.                $old = $values[1];                 // backup old if you want to output
  404.                $values[1] += 45.0;                // change value
  405.  
  406.                // replace old text with new text, containing the new values
  407.                $new  = str_replace($match[2], implode(", ", $values), $match[0]);
  408.                $data = str_replace($match[0], $new, $data);
  409.  
  410.                #echo "Left Shoulder Bend: $old -> ".$values[1].LBR;
  411.             }
  412.             // Shoulder bend right
  413.             if (preg_match("/rShldrBend:\?rotation\/z\/value\"(.*)\"keys\"\s*:\s\[\s*\[(.*)\]\s*\]\n/isU", $data, $match))  {
  414.                $values = explode(",", $match[2]);
  415.                $values[0] = trim($values[0]);
  416.                $values[1] = trim($values[1]);
  417.                $old = $values[1];
  418.                $values[1] -= 45.0;
  419.  
  420.                $new  = str_replace($match[2], implode(", ", $values), $match[0]);
  421.                $data = str_replace($match[0], $new, $data);
  422.  
  423.                #echo "Right Shoulder Bend: $old -> ".$values[1].LBR;
  424.             }
  425.  
  426.             // Thigh sidebyside left
  427.             if (preg_match("/lThighBend:\?rotation\/z\/value\"(.*)\"keys\"\s*:\s\[\s*\[(.*)\]\s*\]\n/isU", $data, $match))  {
  428.                $values = explode(",", $match[2]);
  429.                $values[0] = trim($values[0]);
  430.                $values[1] = trim($values[1]);
  431.                $old = $values[1];
  432.                $values[1] -= 6.0;
  433.  
  434.                $new  = str_replace($match[2], implode(", ", $values), $match[0]);
  435.                $data = str_replace($match[0], $new, $data);
  436.  
  437.                #echo "Left Thigh Bend: $old -> ".$values[1].LBR;
  438.             }
  439.             // Thigh sidebyside right
  440.             if (preg_match("/rThighBend:\?rotation\/z\/value\"(.*)\"keys\"\s*:\s\[\s*\[(.*)\]\s*\]\n/isU", $data, $match))  {
  441.                $values = explode(",", $match[2]);
  442.                $values[0] = trim($values[0]);
  443.                $values[1] = trim($values[1]);
  444.                $old = $values[1];
  445.                $values[1] += 6.0;
  446.  
  447.                $new  = str_replace($match[2], implode(", ", $values), $match[0]);
  448.                $data = str_replace($match[0], $new, $data);
  449.  
  450.                #echo "Right Thigh Bend: $old -> ".$values[1].LBR;
  451.             }
  452.         }
  453.  
  454.         /*----------  write back changed file  ----------*/
  455.         file_put_contents($file, $data);
  456.  
  457.         /*----------  if it was a compressed file, cleanup tmp data  ----------*/
  458.         if ($src != $file)  {
  459.             // remove temps
  460.             unlink($tmp_path . basename($file));
  461.             rmdir($tmp_path);
  462.         }
  463.  
  464.         return true;
  465.     }
  466.  
  467.     // -------------------------------------------------------------------- //
  468.  
  469.     /**
  470.      *
  471.      * Check if given path exists, if not, create it.
  472.      * not tested on windows!
  473.      *
  474.      */
  475.     private static function _s_checkPath($path)  {
  476.         $path = str_replace(SEP.SEP, SEP, $path);
  477.         if (!preg_match("/".preg_quote(SEP, '/')."/", $path)) return false;
  478.         $tmp = explode(SEP, $path);
  479.         $new = array();
  480.         foreach($tmp as $key => $value) {
  481.             if ($value == '..')      array_pop($new);
  482.             else if ($value == '.')  continue;
  483.             else if (strlen($value) || !$key) array_push($new, $value);
  484.         }
  485.         $chk = "";
  486.         foreach($tmp as $key => $value)  {
  487.             $chk .= $value.SEP;
  488.             if (!is_dir($chk)) {
  489.                 if (!(mkdir($chk, 0777)))  {
  490.                     echo "FAILED to create dir: $chk".LBR;
  491.                     return false;
  492.                 }
  493.             }
  494.         }
  495.         return true;
  496.     }
  497.  
  498.     // -------------------------------------------------------------------- //
  499.  
  500.     /**
  501.      *
  502.      * Return extension of file (without dot)
  503.      *
  504.      */
  505.     private static function _s_getFileExtension($filename)  {
  506.         $filename = trim($filename);
  507.         if (!mb_strlen($filename)) return '';
  508.         if (($p = strripos($filename, '.')) === false) return '';
  509.         return mb_substr($filename, $p+1);
  510.     }
  511.  
  512.     // -------------------------------------------------------------------- //
  513.     private static function _s_readString($str)  {
  514.         if (PHP_OS == 'WINNT')  {
  515.             echo $str." :";
  516.             $k = stream_get_line(STDIN, 1024, PHP_EOL);
  517.         } else $k = readline($str." :");
  518.         return $k;
  519.     }
  520.  
  521.     private static function _s_askYesNo($question)  {
  522.         do {
  523.             if (PHP_OS == 'WINNT')  {
  524.                 echo $question." (y/n): ";
  525.                 $k = stream_get_line(STDIN, 1024, PHP_EOL);
  526.             } else $k = readline($question." (y/n): ");
  527.         } while ($k != 'y' && $k != 'j' && $k != 'n');
  528.         return $k;
  529.     }
  530.  
  531.     private static function _s_askYesNoExt($question, $options)  {
  532.         $option_string = implode(", ", $options);
  533.         do {
  534.             if (PHP_OS == 'WINNT')  {
  535.                 echo $question." ($option_string): ";
  536.                 $k = stream_get_line(STDIN, 1024, PHP_EOL);
  537.             } else $k = readline($question." ($option_string): ");
  538.         } while (!isset($options[strtolower($k)]));
  539.         return strtolower($k);
  540.     }
  541.  
  542.  
  543. }
  544.  
  545.  
  546. // -------------------------------------------------------------------- //
  547. // -------------------------------------------------------------------- //
  548.  
  549.  
  550. echo "******************************************************".LBR;
  551. echo "* DAZ3D - Convert Poses from Genesis 3 to Genesis 8  *".LBR;
  552. echo "* v1.21                                              *".LBR;
  553. echo "******************************************************".LBR;
  554. echo LBR;
  555. echo "This will convert G3 Poses from a source directory".LBR;
  556. echo "and copy them to the specified target directory.".LBR.LBR;
  557.  
  558. /*----------  Check arguments  ----------*/
  559. if (!isset($argv[1]) || !isset($argv[2]))
  560.     die("Usage: php ".basename(__FILE__)." <path_to_source_library> <path_to_target_library>".LBR.LBR);
  561.  
  562.  
  563. /*----------  Check dependencies  ----------*/
  564. if (!DAZ_ConvertPose::s_checkDependencies())
  565.     exit;
  566.  
  567. /*----------  Get us a new instance of the converter  ----------*/
  568. $converter = new DAZ_ConvertPose();
  569.  
  570. /*----------  Set source path  ----------*/
  571. if (!$converter->setPathSource($argv[1]))
  572.     die("Source path does not exist or is invalid (contains no 'People') folder:".LBR."\t".$arv[1].LBR.LBR);
  573.  
  574. /*----------  Set destination path  ----------*/
  575. if (!$converter->setPathDestination($argv[2]))
  576.     die("Destination path does not exist:".LBR."\t".$arv[2].LBR.LBR);
  577.  
  578. /*----------  Run the converter  ----------*/
  579. $converter->run();
  580.  
  581.  
  582. /*----------  Done, eh? :)  ----------*/
  583. die("All done, going home...".LBR.LBR);
  584.  
  585. ?>
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement