swf2ass/src/SWFTreeProcessor.php

260 lines
8.4 KiB
PHP

<?php
namespace swf2ass;
use swf2ass\actions\ActionList;
use swf2ass\actions\GoToFrameAction;
use swf2ass\actions\NextFrameAction;
use swf2ass\actions\PlayAction;
use swf2ass\actions\PreviousFrameAction;
use swf2ass\actions\StopAction;
class SWFTreeProcessor {
protected ViewLayout $layout;
protected ObjectCollection $objects;
protected ?array $tags;
protected int $index = 0;
protected int $frame;
protected ?ViewFrame $lastFrame = null;
protected bool $playing = true;
protected int $loops = 0;
public function __construct(int $objectId, ?array $tags, ?ObjectCollection $objects = null) {
$this->objects = $objects ?? new ObjectCollection();
$this->frame = 0;
$this->tags = $tags;
$this->layout = new ViewLayout($objectId, null);
}
public function getTags() : array{
return $this->tags;
}
public function getFrame(): int {
return $this->frame;
}
protected function next() {
++$this->index;
}
protected function current(): ?array {
return $this->tags[$this->index] ?? null;
}
public function getObjectCollection(): ObjectCollection {
return $this->objects;
}
protected function process(ActionList $actionList): ?string {
$node = $this->current();
if ($node === null) {
return null;
}
switch ($node["tagType"]) {
case "DefineMorphShape":
case "DefineMorphShape2":
if($this->loops > 0){
break;
}
$shape = MorphShapeDefinition::fromArray($node);
$this->objects->add($shape);
break;
case "DefineShape":
case "DefineShape2":
case "DefineShape3":
case "DefineShape4":
case "DefineShape5":
if($this->loops > 0){
break;
}
$shape = ShapeDefinition::fromArray($node);
$this->objects->add($shape);
break;
case "DefineSprite":
if($this->loops > 0){
break;
}
$objectID = $node["spriteId"];
$framesCount = $node["frameCount"];
$sprite = new SpriteDefinition($objectID, new SWFTreeProcessor($objectID, $node["tags"], $this->objects));
$this->objects->add($sprite);
break;
case "DefineBitsLossless":
case "DefineBitsLossless2":
if($this->loops > 0){
break;
}
break; //TODO
$bitmap = BitmapDefinition::fromArray($node);
$this->objects->add($bitmap);
break;
case "DefineBitsJPEG2":
case "DefineBitsJPEG3":
if($this->loops > 0){
break;
}
break; //TODO
$bitmap = JPEGBitmapDefinition::fromArray($node);
$this->objects->add($bitmap);
break;
case "RemoveObject":
case "RemoveObject2":
$this->layout->remove($node["depth"]);
break;
case "PlaceObject2":
case "PlaceObject3":
$depth = $node["depth"];
$objectID = $node["characterId"] ?? null;
$clipDepth = $node["clipDepth"] ?? null;
$replace = $node["placeFlagMove"] === 1;
$object = $objectID === null ? $this->layout->get($depth) : $this->objects->get($objectID);
if ($object === null) {
var_dump("Object oid:$objectID depth:$depth not found");
/*if($replace){
$this->layout->remove($depth);
}*/
//TODO: insert bogus one
$this->layout->remove($depth);
break;
}
$ratio = isset($node["ratio"]) ? $node["ratio"] / 65535 : null;
$transform = isset($node["matrix"]) ? MatrixTransform::fromSWFArray($node["matrix"]) : null;
$colorTransform = isset($node["colorTransform"]) ? ColorTransform::fromArray($node["colorTransform"]) : null;
$currentObject = $this->layout->get($depth);
if ($replace and $currentObject !== null and $currentObject->getObjectId() === $object->getObjectId()) {
if ($transform !== null) {
$currentObject->setMatrixTransform($transform);
}
if ($colorTransform !== null) {
$currentObject->setColorTransform($colorTransform);
}
if($ratio !== null){
$currentObject->setRatio($ratio);
}
break;
}
$view = $clipDepth !== null ? new ClippingViewLayout($clipDepth, $objectID, $object->getSafeObject(), $this->layout) : new ViewLayout($objectID, $object->getSafeObject(), $this->layout);
$view->setMatrixTransform($transform);
$view->setColorTransform($colorTransform);
$view->setRatio($ratio);
if ($replace) {
$this->layout->replace($depth, $view);
} else {
$this->layout->place($depth, $view);
}
break;
case "ShowFrame":
break;
case "EndTag":
break;
case "DoAction":
foreach ($node["actions"] as $action){
switch ($action["actionName"]){
case "ActionStop":
$actionList->actions[] = new StopAction();
break;
case "ActionPlay":
$actionList->actions[] = new PlayAction();
break;
/*case "ActionGotoFrame":
$actionList->actions[] = new GoToFrameAction($action["actionData"]["frame"]);
break;*/
case "ActionNextFrame":
$actionList->actions[] = new NextFrameAction();
break;
/*case "ActionPreviousFrame":
$actionList->actions[] = new PreviousFrameAction();
break;*/
case null:
break;
default:
var_dump($action);
var_dump("NOT IMPLEMENTED: ACTION " . $action["actionName"]);
break;
}
}
break;
default:
//TODO
var_dump($node["tagType"]);
}
return $node["tagType"];
}
public function getViewLayout(): ViewLayout {
return $this->layout;
}
public function isPlaying() : bool{
return $this->playing;
}
public function getLoops() : int{
return $this->loops;
}
public function nextFrame(): ?ViewFrame {
$actions = new ActionList();
if(!$this->playing){
return $this->lastFrame;
}
while (($nodeName = $this->process($actions)) !== null) {
$this->next();
if ($nodeName === "ShowFrame") {
break;
} else if ($nodeName === "End" and $this->frame === 0) {
break;
}
}
if ($nodeName === null) { //Loop again
$this->loops++;
$this->frame = 0;
$this->index = 0;
$this->layout = new ViewLayout($this->layout->getObjectId(), null);
return $this->lastFrame !== null ? $this->nextFrame() : null;
}
++$this->frame;
$frame = $this->lastFrame = $this->layout->nextFrame($actions);
foreach ($actions->actions as $action){
if($action instanceof StopAction){
$this->playing = false;
}else if($action instanceof PlayAction){
$this->playing = true;
}else if($action instanceof NextFrameAction){
return $this->nextFrame();
}
}
return $frame;
}
}