"simple" implementation of Actions

This commit is contained in:
DataHoarder 2022-01-08 20:10:29 +01:00
parent 32980ce1c0
commit 54ed3bc110
21 changed files with 245 additions and 66 deletions

View file

@ -1,9 +0,0 @@
<?php
namespace swf2ass;
class ActionList {
}

View file

@ -91,4 +91,8 @@ class BitmapDefinition implements ObjectDefinition {
return (string)$im;
}
public function getSafeObject() : BitmapDefinition{
return $this;
}
}

View file

@ -3,6 +3,8 @@
namespace swf2ass;
use swf2ass\actions\ActionList;
class ClippingViewLayout extends ViewLayout {
private int $clipDepth;

View file

@ -9,8 +9,6 @@ class MorphShapeDefinition implements ObjectDefinition {
public DrawPathList $startShapeList;
public DrawPathList $endShapeList;
public float $ratio = 0;
public function __construct(int $id, Rectangle $startBounds, Rectangle $endBounds, DrawPathList $startShapeList, DrawPathList $endShapeList) {
$this->id = $id;
@ -185,4 +183,8 @@ class MorphShapeDefinition implements ObjectDefinition {
return new MorphShapeDefinition($element["characterId"], Rectangle::fromArray($element["startBounds"]), Rectangle::fromArray($element["endBounds"]), $start, $end);
}
public function getSafeObject() : MorphShapeDefinition{
return $this;
}
}

View file

@ -6,5 +6,7 @@ interface ObjectDefinition {
public function getObjectId(): int;
public function getSafeObject() : ObjectDefinition;
public function getShapeList(float $ratio): DrawPathList;
}

View file

@ -4,6 +4,7 @@ namespace swf2ass;
use swf\SWF;
use swf2ass\actions\ActionList;
class SWFProcessor extends SWFTreeProcessor {
const BACKGROUND_OBJECT_ID = 0;
@ -55,7 +56,7 @@ class SWFProcessor extends SWFTreeProcessor {
return $this->audio;
}
protected function process(): ?string {
protected function process(ActionList $actionList): ?string {
$node = $this->current();
if ($node === null) {
return null;
@ -71,6 +72,8 @@ class SWFProcessor extends SWFTreeProcessor {
$this->audio = AudioStream::fromSoundStreamHeadTag($node);
return $node["tagType"];
case "DefineSound":
$this->audio = new AudioStream(0, 0, 0, 0);
$this->audio->setStartFrame($this->getFrame());
//TODO $this->audio = (object)["node" => $node, "start" => $this->getFrame(), "content" => []];
return $node["tagType"];
case "SoundStreamBlock":
@ -83,17 +86,20 @@ class SWFProcessor extends SWFTreeProcessor {
return $node["tagType"];
}
return parent::process();
return parent::process($actionList);
}
public function nextFrameOutput(): ?FrameInformation {
$actions = $actions ?? new ActionList();
$frame = $this->nextFrame($actions);
$frame = $this->nextFrame();
if ($frame === null) {
return null;
}
if(!$this->isPlaying() and ($this->audio === null or $this->audio->getStartFrame() === null)){ //Force play till finding audio
$this->playing = true;
}
//TODO: actions?
$frame->addChild(self::BACKGROUND_OBJECT_DEPTH, new ViewFrame(self::BACKGROUND_OBJECT_ID, new DrawPathList([DrawPath::fill($this->background, new Shape($this->getViewPort()->draw()))])));

View file

@ -3,6 +3,13 @@
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;
@ -12,6 +19,10 @@ class SWFTreeProcessor {
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;
@ -19,6 +30,10 @@ class SWFTreeProcessor {
$this->layout = new ViewLayout($objectId, null);
}
public function getTags() : array{
return $this->tags;
}
public function getFrame(): int {
return $this->frame;
}
@ -35,7 +50,7 @@ class SWFTreeProcessor {
return $this->objects;
}
protected function process(): ?string {
protected function process(ActionList $actionList): ?string {
$node = $this->current();
if ($node === null) {
return null;
@ -59,22 +74,8 @@ class SWFTreeProcessor {
$objectID = $node["spriteId"];
$framesCount = $node["frameCount"];
$spriteTree = new SWFTreeProcessor($objectID, $node["tags"], $this->objects);
$actions = new ActionList();
/** @var ViewFrame[] $frames */
$frames = [];
while (($frame = $spriteTree->nextFrame($actions)) !== null) {
$frames[] = $frame;
}
if(count($frames) === 0){
var_dump($node);
var_dump("sprite $objectID had no valid frames!");
break;
}
$sprite = new SpriteDefinition($objectID, $frames);
$sprite = new SpriteDefinition($objectID, new SWFTreeProcessor($objectID, $node["tags"], $this->objects));
$this->objects->add($sprite);
break;
@ -140,7 +141,7 @@ class SWFTreeProcessor {
break;
}
$view = $clipDepth !== null ? new ClippingViewLayout($clipDepth, $objectID, $object, $this->layout, $ratio) : new ViewLayout($objectID, $object, $this->layout, $ratio);
$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);
@ -156,6 +157,33 @@ class SWFTreeProcessor {
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"]);
@ -168,8 +196,16 @@ class SWFTreeProcessor {
return $this->layout;
}
public function nextFrame(ActionList $actions): ?ViewFrame {
while (($nodeName = $this->process()) !== null) {
public function isPlaying() : bool{
return $this->playing;
}
public function nextFrame(): ?ViewFrame {
$actions = new ActionList();
if(!$this->playing){
return $this->lastFrame;
}
while (($nodeName = $this->process($actions)) !== null) {
$this->next();
if ($nodeName === "ShowFrame") {
@ -179,12 +215,26 @@ class SWFTreeProcessor {
}
}
if ($nodeName === null) {
return null;
if ($nodeName === null) { //Loop again
$this->frame = 0;
$this->index = 0;
$this->layout = new ViewLayout($this->layout->getObjectId(), null);
return $this->lastFrame !== null ? $this->nextFrame() : null;
}
++$this->frame;
//TODO $this->layout->hasFrame();
return $this->layout->nextFrame($actions);
$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;
}
}

View file

@ -33,4 +33,8 @@ class ShapeDefinition implements ObjectDefinition {
return new ShapeDefinition($element["shapeId"], Rectangle::fromArray($element["shapeBounds"]), $drawPathList);
}
public function getSafeObject() : ShapeDefinition{
return $this;
}
}

View file

@ -4,20 +4,15 @@ namespace swf2ass;
class SpriteDefinition implements MultiFrameObjectDefinition {
public int $id;
/** @var ViewFrame[] */
public array $frames;
public int $frameCounter;
private SWFTreeProcessor $swf;
/**
* @param int $id
* @param ViewFrame[] $frames
* @param int $frameCounter
*/
public function __construct(int $id, array $frames, int $frameCounter = 0) {
private ?ViewFrame $currentFrame = null;
public function __construct(int $id, SWFTreeProcessor $swf) {
$this->id = $id;
$this->frames = $frames;
$this->frameCounter = $frameCounter % count($this->frames);
$this->swf = $swf;
}
public function getObjectId(): int {
@ -26,26 +21,19 @@ class SpriteDefinition implements MultiFrameObjectDefinition {
public function getShapeList(?float $ratio): DrawPathList {
$list = new DrawPathList();
foreach ($this->frames[$this->frameCounter]->render(0, [], null, null)->getObjects() as $object) {
$list = $list->merge($object->drawPathList);
if($this->currentFrame !== null){
foreach ($this->currentFrame->render(0, [], null, null)->getObjects() as $object) {
$list = $list->merge($object->drawPathList);
}
}
return $list;
}
public function getFrameCounter(): int {
return $this->frameCounter;
}
public function getFrame(int $frameNumber): ?ViewFrame {
return $this->frames[$frameNumber] ?? null;
}
public function nextFrame(): ViewFrame {
$f = $this->frames[$this->frameCounter];
++$this->frameCounter;
if ($this->frameCounter >= count($this->frames)) {
$this->frameCounter = 0;
}
return $f;
return $this->currentFrame = $this->swf->nextFrame();
}
public function getSafeObject() : SpriteDefinition{
return new SpriteDefinition($this->id, new SWFTreeProcessor($this->id, $this->swf->getTags(), $this->swf->getObjectCollection()));
}
}

View file

@ -3,6 +3,8 @@
namespace swf2ass;
use swf2ass\actions\ActionList;
class ViewLayout {
private ?ViewLayout $parent;

8
src/actions/Action.php Normal file
View file

@ -0,0 +1,8 @@
<?php
namespace swf2ass\actions;
interface Action {
}

View file

@ -0,0 +1,11 @@
<?php
namespace swf2ass\actions;
class ActionList {
/** @var Action[] */
public array $actions = [];
}

View file

@ -0,0 +1,17 @@
<?php
namespace swf2ass\actions;
class GoToFrameAction implements Action {
private int $frame;
public function __construct(int $frame){
$this->frame = $frame;
}
public function getFrame() : int{
return $this->frame;
}
}

View file

@ -0,0 +1,17 @@
<?php
namespace swf2ass\actions;
class GoToLabelAction implements Action {
private string $label;
public function __construct(string $label){
$this->label = $label;
}
public function getLabel() : string{
return $this->label;
}
}

View file

@ -0,0 +1,7 @@
<?php
namespace swf2ass\actions;
class NextFrameAction implements Action {
}

View file

@ -0,0 +1,9 @@
<?php
namespace swf2ass\actions;
class PlayAction implements Action {
}

View file

@ -0,0 +1,7 @@
<?php
namespace swf2ass\actions;
class PreviousFrameAction implements Action {
}

View file

@ -0,0 +1,17 @@
<?php
namespace swf2ass\actions;
class SetTargetAction implements Action {
private string $target;
public function __construct(string $target){
$this->target = $target;
}
public function getTarget() : string{
return $this->target;
}
}

View file

@ -0,0 +1,9 @@
<?php
namespace swf2ass\actions;
class StopAction implements Action {
}

View file

@ -0,0 +1,23 @@
<?php
namespace swf2ass\actions;
class WaitForFrameAction implements Action {
private int $frame;
private int $skipCount;
public function __construct(int $frame, int $skipCount){
$this->frame = $frame;
$this->skipCount = $skipCount;
}
public function getFrame() : int{
return $this->frame;
}
public function getSkipCount() : int{
return $this->skipCount;
}
}

View file

@ -125,6 +125,10 @@ if ($swf->header["signature"]) {
$keyFrameInterval = 10 * $processor->getFrameRate(); //kf every 10 seconds TODO: make this dynamic, per-shape
$lastFrame = null;
while(($frame = $processor->nextFrameOutput()) !== null){
$lastFrame = $frame;
if(!$processor->isPlaying()){
break;
}
$audio = $processor->getAudio();
if($audio !== null and $frameOffset === 0){
if($audio->getStartFrame() === null){
@ -132,7 +136,6 @@ if ($swf->header["signature"]) {
}
$frameOffset = $audio->getStartFrame();
}
$lastFrame = $frame;
$frame->setFrameOffset($frameOffset);