2020-09-20 13:16:51 +00:00
< ? php
require_once __DIR__ . " /vendor/autoload.php " ;
2022-01-05 21:19:15 +00:00
$settings = [
" videoScaleMultiplier " => 1 , //TODO: not finished, leave at 1
2022-01-07 10:51:07 +00:00
//TODO: make this actually interpolate smooth transitions in a baked way
" videoRateMultiplier " => 1 , //Sets the "framerate" multiplier for output video file, not lines. Helps with smoothing transitions if enabled.
2022-01-05 21:19:15 +00:00
" bakeTransforms " => false , //TODO: fix ASS matrix transform rendering and remove this
" timerSpeed " => 100 , //NOTE: libass does not implement "Timer:", which is used by this setting. Leave at 100 by default
" timePrecision " => 2 , //NOTE: libass does not implement anything different from 2. Leave at 2 by default,
2022-01-07 10:51:07 +00:00
" smoothTransitions " => false , //All transitions will happen smoothly from start till finish instead of happening instantly on frame thresholds. NOTE: this can break objects that did not get selected to be animated, or matrix transforms
2023-08-02 23:48:26 +00:00
" gradientSlices " => /*24,*/ \swf2ass\Gradient :: AUTO_SLICES ,
2022-01-05 21:19:15 +00:00
];
2020-09-20 13:16:51 +00:00
2022-01-05 21:19:15 +00:00
$swfContent = file_get_contents ( $argv [ 1 ]);
$signature = hash ( " sha256 " , $swfContent , false );
2022-01-04 00:15:31 +00:00
2022-01-05 21:19:15 +00:00
$swf = new \swf\SWF ( $swfContent );
$swfContent = null ;
unset ( $swfContent );
2022-01-04 00:15:31 +00:00
2020-09-20 13:16:51 +00:00
$fp = fopen ( $argv [ 2 ], " w+ " );
2021-12-26 18:06:27 +00:00
2022-01-05 21:19:15 +00:00
$fromFrame = isset ( $argv [ 3 ]) ? ( int ) $argv [ 3 ] : null ;
$frameEnd = isset ( $argv [ 4 ]) ? ( int ) $argv [ 4 ] : null ;
if ( $fromFrame !== null and $frameEnd === null ){
$frameEnd = $fromFrame ;
}
class RemovalEntry {
//TODO: accept names as well?
public ? int $objectId ;
public ? array $depth ;
/**
* @ param int $objectId
* @ param int [] $depth
*/
public function __construct ( ? int $objectId , ? array $depth ){
$this -> objectId = $objectId ;
$this -> depth = $depth ;
}
public function equals ( \swf2ass\RenderedObject $object ) : bool {
return ( $this -> objectId === null or $object -> objectId === $this -> objectId ) and ( $this -> depth === null or ( count ( $object -> depth ) >= count ( $this -> depth ) and array_slice ( $object -> depth , 0 , count ( $this -> depth )) === $this -> depth ));
}
}
$frameOffset = 0 ;
$objectRemovalEntries = [];
//TODO: make this a JSON file elsewhere
$knownFlashSignatures = [
" 52e75b7d6831293ebf4e5b28574a60f5bce10b1eae8afa4e69ab213a98b0b008 " => [
" name " => " IJSW.swf " ,
" remove " => [
//removes playback menus
new RemovalEntry ( null /*2*/ , [ 0 , 31 ]),
new RemovalEntry ( null /*3*/ , [ 0 , 145 ]),
new RemovalEntry ( null /*69*/ , [ 0 , 146 ]),
]
2022-01-07 10:51:07 +00:00
],
" 229d7569ebf3b3b04fb03aa86162ab646d96be0353e8f4be32b1f6bf4e96af4e " => [
" name " => " cirno's_arithmetic_school.swf " ,
" remove " => [
//remove lyrics button/mask
new RemovalEntry ( null /*30*/ , [ 0 , 259 ]),
new RemovalEntry ( null /*31*/ , [ 0 , 264 ]),
]
],
" 1203faf6504edf4845e72b957ce6b5c5d92597ecc0328aa36eacaef32eb7125a " => [
" name " => " cirno's_arithmetic_school_2011.swf " ,
" remove " => [
//remove lyrics button/mask
new RemovalEntry ( null /*30*/ , [ 0 , 229 ]),
new RemovalEntry ( null /*31*/ , [ 0 , 234 ]),
]
],
" 3bdc7f8bbfb648e825f79dbe6095288871b1780b1d37f8cd2ad4c50cae40b5ca " => [
" name " => " IOSYS_CirnoENG.swf " ,
" remove " => [
//remove lyrics button/mask
new RemovalEntry ( null /*33*/ , [ 0 , 1721 ]),
new RemovalEntry ( null /*34*/ , [ 0 , 1752 ]),
]
],
2022-01-05 21:19:15 +00:00
];
if ( isset ( $knownFlashSignatures [ $signature ])){
$e = $knownFlashSignatures [ $signature ];
echo " Found known signature for " . $e [ " name " ] . " , adding rules \n " ;
$objectRemovalEntries = $e [ " remove " ];
if ( isset ( $e [ " frameOffset " ])){
$frameOffset = $e [ " frameOffset " ];
}
if ( isset ( $e [ " frameEnd " ])){
$frameEnd = $e [ " frameEnd " ];
}
}
//Function to decide whether to display object or not
function filterObject ( \swf2ass\RenderedObject $object ) : bool {
global $objectRemovalEntries ;
/** @var RemovalEntry[] $objectRemovalEntries */
foreach ( $objectRemovalEntries as $entry ){
if ( $entry -> equals ( $object )){
return true ;
}
}
return false ;
}
2020-09-20 13:16:51 +00:00
2022-01-04 00:15:31 +00:00
2022-01-05 21:19:15 +00:00
$testVectors = [];
2022-01-04 00:15:31 +00:00
2021-12-31 05:41:35 +00:00
if ( $swf -> header [ " signature " ]) {
$processor = new \swf2ass\SWFProcessor ( $swf );
2022-01-07 10:51:07 +00:00
\swf2ass\ass\ASSRenderer :: setSettings ( $settings );
$assRenderer = new \swf2ass\ass\ASSRenderer ( $processor -> getFrameRate (), $processor -> getViewPort ());
2021-12-29 00:12:28 +00:00
2022-01-01 09:54:27 +00:00
$keyFrameInterval = 10 * $processor -> getFrameRate (); //kf every 10 seconds TODO: make this dynamic, per-shape
2021-12-29 20:57:52 +00:00
$lastFrame = null ;
2021-12-29 00:12:28 +00:00
while (( $frame = $processor -> nextFrameOutput ()) !== null ){
2022-01-08 19:10:29 +00:00
$lastFrame = $frame ;
2022-01-08 19:29:26 +00:00
if ( ! $processor -> isPlaying () or $processor -> getLoops () > 0 ){
2022-01-08 19:10:29 +00:00
break ;
}
2021-12-29 00:12:28 +00:00
$audio = $processor -> getAudio ();
if ( $audio !== null and $frameOffset === 0 ){
2022-01-07 20:03:03 +00:00
if ( $audio -> getStartFrame () === null ){
2021-12-29 00:12:28 +00:00
continue ;
}
2022-01-07 20:03:03 +00:00
$frameOffset = $audio -> getStartFrame ();
2021-12-29 00:12:28 +00:00
}
$frame -> setFrameOffset ( $frameOffset );
2021-12-29 20:57:52 +00:00
$rendered = $frame -> getFrame () -> render ( 0 , [], null , null );
2022-01-05 21:19:15 +00:00
if ( $frame -> getFrameNumber () === 0 ){
foreach ( $rendered -> getObjects () as $ob ){
echo " frame 0: object { $ob -> objectId } depth: " . implode ( " , " , $ob -> depth ) . PHP_EOL ;
}
}
$filteredRendered = new \swf2ass\RenderedFrame ();
2021-12-29 00:12:28 +00:00
$drawCalls = 0 ;
$drawItems = 0 ;
2022-01-05 21:19:15 +00:00
$filteredObjects = 0 ;
2021-12-29 00:12:28 +00:00
$clipCalls = 0 ;
$clipItems = 0 ;
foreach ( $rendered -> getObjects () as $object ){
2022-01-05 21:19:15 +00:00
if ( filterObject ( $object )){
++ $filteredObjects ;
continue ;
}
2021-12-29 00:12:28 +00:00
if ( $object -> clip !== null ){
++ $clipCalls ;
2022-01-04 00:15:31 +00:00
$clipItems += count ( $object -> clip -> getShape () -> getRecords ());
2021-12-29 00:12:28 +00:00
}
foreach ( $object -> drawPathList -> commands as $path ){
++ $drawCalls ;
2022-01-04 00:15:31 +00:00
$drawItems += count ( $path -> commands -> getRecords ());
2021-12-29 00:12:28 +00:00
}
2022-01-05 21:19:15 +00:00
$filteredRendered -> add ( $object );
}
echo " === frame " . $frame -> getFrameNumber () . " / " . $processor -> getExpectedFrameCount () . " ~ $frameOffset : Depth count: " . count ( $frame -> getFrame () -> getDepthMap ()) . " :: Object count: " . count ( $filteredRendered -> getObjects ()) . " :: Paths: $drawCalls draw calls, $drawItems items :: Filtered: $filteredObjects :: Clips: $clipCalls draw calls, $clipItems items " . PHP_EOL ;
if ( $fromFrame !== null ){
if ( $frame -> getFrameNumber () < $fromFrame ){
continue ;
} else {
foreach ( $rendered -> getObjects () as $object ){
$count = 0 ;
foreach ( $object -> drawPathList -> commands as $i => $path ){
foreach ( $path -> commands -> getRecords () as $j => $record ){
$v = [
" objectId " => $object -> objectId ,
" depth " => implode ( " . " , $object -> getDepth ()) . " [ $i ][ $j ] " ,
" transform " => $object -> matrixTransform -> toArray ( false )
];
if ( $record instanceof \swf2ass\MoveRecord or $record instanceof \swf2ass\LineRecord ){
$v [ " vector " ] = $record -> start -> toArray ();
$testVectors [] = $v ;
$v [ " vector " ] = $record -> to -> toArray ();
$testVectors [] = $v ;
} else if ( $record instanceof \swf2ass\QuadraticCurveRecord ){
$v [ " vector " ] = $record -> start -> toArray ();
$testVectors [] = $v ;
$v [ " vector " ] = $record -> control -> toArray ();
$testVectors [] = $v ;
$v [ " vector " ] = $record -> anchor -> toArray ();
$testVectors [] = $v ;
}
break ;
}
break ;
}
}
}
2021-12-29 00:12:28 +00:00
}
2022-01-05 21:19:15 +00:00
foreach ( $assRenderer -> renderFrame ( $frame , $filteredRendered ) as $line ){
2021-12-29 00:12:28 +00:00
fwrite ( $fp , $line . " \n " );
}
if ( $frame -> getFrameNumber () > 0 and $frame -> getFrameNumber () % $keyFrameInterval === 0 ){
2021-12-29 20:57:52 +00:00
foreach ( $assRenderer -> flush ( $frame ) as $line ){
2021-12-29 00:12:28 +00:00
fwrite ( $fp , $line . " \n " );
}
}
2022-01-05 21:19:15 +00:00
if ( $frameEnd !== null and $frame -> getFrameNumber () >= $frameEnd ){
break ;
}
2021-12-29 00:12:28 +00:00
}
2022-01-08 19:29:26 +00:00
2021-12-29 20:57:52 +00:00
foreach ( $assRenderer -> flush ( $lastFrame ) as $line ){
2021-12-29 00:12:28 +00:00
fwrite ( $fp , $line . " \n " );
}
2022-01-07 20:03:03 +00:00
if ( $processor -> getAudio () !== null and $processor -> getAudio () -> getFormat () === \swf2ass\AudioStream :: FORMAT_MP3 ){
$audioFp = fopen ( $argv [ 2 ] . " .mp3 " , " w+ " );
fwrite ( $audioFp , $processor -> getAudio () -> getAudioData ());
fclose ( $audioFp );
}
2020-09-20 13:16:51 +00:00
}
2022-01-05 21:19:15 +00:00
if ( count ( $testVectors ) > 0 ){
file_put_contents ( $argv [ 2 ] . " .test.json " , json_encode ( $testVectors , JSON_PRETTY_PRINT ));
}
2021-12-29 00:12:28 +00:00
fclose ( $fp );
2020-09-20 13:16:51 +00:00