Last active
November 17, 2021 06:19
-
-
Save esterTion/e24d7142e0fa0bcc6ef11ee586ce5235 to your computer and use it in GitHub Desktop.
Some sort of spine skeleton binary data to json converter
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php | |
| /* | |
| This converter is not finished, and will not be finished. | |
| If you are interested in previewing, use this modified WebGL runtime lib | |
| https://github.com/esterTion/spine-runtimes/tree/3.6/spine-ts | |
| Change the SkeletonJson class to SkeletonBinary to load binary skel data | |
| Usage demo: https://redive.estertion.win/spine/ | |
| */ | |
| /* | |
| Some sort of skel binary to json converter | |
| a few fields are not processed, and will throw error when encountered | |
| Used for converting resource from priconne-redive | |
| */ | |
| if (count(get_included_files()) == 1) define ('TEST_SUITE', __FILE__); | |
| abstract class Stream { | |
| abstract protected function read($length); | |
| abstract public function seek($position); | |
| abstract public function position(); | |
| abstract protected function getPos(); | |
| abstract protected function setPos($pos); | |
| public function __get($name) { | |
| switch($name) { | |
| case 'position': return $this->getPos(); | |
| case 'bool': return $this->readBoolean(); | |
| case 'byte': return $this->readData(1); | |
| case 'short': return $this->readInt16(); | |
| case 'ushort': return $this->readUint16(); | |
| case 'long': return $this->readInt32(); | |
| case 'ulong': return $this->readUint32(); | |
| case 'longlong': return $this->readInt64(); | |
| case 'ulonglong': return $this->readUint64(); | |
| case 'float': return $this->readFloat(); | |
| case 'double': return $this->readDouble(); | |
| case 'string': return $this->readStringToNull(); | |
| case 'line': return $this->readStringToReturn(); | |
| default: throw new Exception("Access undefined field ${name} of class ".get_class($this)); | |
| } | |
| } | |
| public function __set($name, $val) { | |
| switch($name) { | |
| case 'position': return $this->setPos($val); | |
| default: throw new Exception("Assign value to undefined field ${name} of class ".get_class($this)); | |
| } | |
| } | |
| public $littleEndian = false; | |
| public $size; | |
| public function readStringToNull() { | |
| $s = ''; | |
| while (ord($char = $this->read(1)) != 0) { | |
| $s .= $char; | |
| } | |
| return $s; | |
| } | |
| public function readStringAt($pos) { | |
| $current = $this->position; | |
| $this->position = $pos; | |
| $data = $this->string; | |
| $this->position = $current; | |
| return $data; | |
| } | |
| public function readStringToReturn() { | |
| $s = ''; | |
| while ($this->position < $this->size && ($char = $this->read(1)) != "\n") { | |
| $s .= $char; | |
| } | |
| return trim($s,"\r"); | |
| } | |
| public function readBoolean() { | |
| return ord($this->byte)>0; | |
| } | |
| public function readInt16() { | |
| $uint = $this->readUint16(); | |
| $sint = unpack('s', pack('S', $uint))[1]; | |
| return $sint; | |
| } | |
| public function readUint16() { | |
| $int = $this->read(2); | |
| if (strlen($int) != 2) return 0; | |
| return unpack($this->littleEndian?'v':'n', $int)[1]; | |
| } | |
| public function readInt32() { | |
| $uint = $this->readUint32(); | |
| $sint = unpack('l', pack('L', $uint))[1]; | |
| return $sint; | |
| } | |
| public function readUint32() { | |
| $int = $this->read(4); | |
| if (strlen($int) != 4) return 0; | |
| return unpack($this->littleEndian?'V':'N', $int)[1]; | |
| } | |
| public function readInt64() { | |
| $uint = $this->readUint64(); | |
| $sint = unpack('q', pack('Q', $uint))[1]; | |
| return $sint; | |
| } | |
| public function readUint64() { | |
| $int = $this->read(8); | |
| if (strlen($int) != 8) return 0; | |
| return unpack($this->littleEndian?'P':'J', $int)[1]; | |
| } | |
| public function readFloat() { | |
| $int = $this->read(4); | |
| if (strlen($int) != 4) return 0; | |
| if (!$this->littleEndian) $int = $int[3].$int[2].$int[1].$int[0]; | |
| return unpack(/*$this->littleEndian?'g':'G'*/ 'f', $int)[1]; | |
| } | |
| public function readDouble() { | |
| $int = $this->read(8); | |
| if (strlen($int) != 8) return 0; | |
| if (!$this->littleEndian) $int = $int[7].$int[6].$int[5].$int[4].$int[3].$int[2].$int[1].$int[0]; | |
| return unpack(/*$this->littleEndian?'e':'E'*/ 'd', $int)[1]; | |
| } | |
| public function readData($size) { | |
| return $this->read($size); | |
| } | |
| public function readDataAt($pos, $size) { | |
| $current = $this->position; | |
| $this->position = $pos; | |
| $data = $this->readData($size); | |
| $this->position = $current; | |
| return $data; | |
| } | |
| public function alignStream($alignment) { | |
| $mod = $this->position % $alignment; | |
| if ($mod != 0) { | |
| $this->position += $alignment - $mod; | |
| } | |
| } | |
| public function readAlignedString($len) { | |
| $string = $this->readData($len); | |
| $this->alignStream(4); | |
| return $string; | |
| } | |
| } | |
| class FileStream extends Stream { | |
| private $f; | |
| function __construct($file) { | |
| $this->f = fopen($file, 'rb+'); | |
| if ($this->f === false) { | |
| throw 'Unable to open file'; | |
| } | |
| $this->size = filesize($file); | |
| } | |
| function __destruct() { | |
| fclose($this->f); | |
| } | |
| protected function read($length) { | |
| return fread($this->f, $length); | |
| } | |
| public function write($newData) { | |
| fwrite($this->f, $newData); | |
| $this->size += strlen($newData); | |
| } | |
| public function seek($position) { | |
| fseek($this->f, $position); | |
| } | |
| public function position() { | |
| return ftell($this->f); | |
| } | |
| protected function getPos() { | |
| return ftell($this->f); | |
| } | |
| protected function setPos($pos) { | |
| fseek($this->f, $pos); | |
| } | |
| } | |
| function readVarInt32($pb, $optimizePositive = true) { | |
| $b=0; | |
| $result=0; | |
| do{ | |
| $b=ord($pb->readData(1)); | |
| $result = $b & 0x7f; | |
| if(!( $b & 0x80 )) | |
| break; | |
| $b=ord($pb->readData(1)); | |
| $result |= ($b & 0x7f)<<7; | |
| if(!( $b & 0x80 )) | |
| break; | |
| $b=ord($pb->readData(1)); | |
| $result |= ($b & 0x7f)<<14; | |
| if(!( $b & 0x80 )) | |
| break; | |
| $b=ord($pb->readData(1)); | |
| $result |= ($b & 0x7f)<<21; | |
| if(!( $b & 0x80 )) | |
| break; | |
| $b=ord($pb->readData(1)); | |
| $result |= ($b & 0x7f)<<28; | |
| if(!( $b & 0x80 )) | |
| break; | |
| for($i=0;$i<5;$i++){ | |
| $b=ord($pb->readData(1)); | |
| if(!( $b & 0x80 )) | |
| break; | |
| } | |
| }while(false); | |
| if (!$optimizePositive) $result = (($result >> 1) ^ -($result & 1)); | |
| if ($result > 0) $result = unpack('l', pack('L', $result))[1]; | |
| return $result; | |
| } | |
| function readString($s) { | |
| $len = readVarInt32($s); | |
| if ($len == 0) return NULL; | |
| if ($len == 1) return ''; | |
| return $s->readData($len - 1); | |
| } | |
| function readColor($s) { | |
| return strtoupper(bin2hex($s->readData(4))); | |
| /*return [ | |
| ord($s->byte), | |
| ord($s->byte), | |
| ord($s->byte), | |
| ord($s->byte) | |
| ];*/ | |
| } | |
| function readCurve($fs) { | |
| $type = ord($fs->byte); | |
| if ($type == CURVE_BEZIER) { | |
| return [ | |
| 'type' => $type, | |
| 'c1' => $fs->float, | |
| 'c2' => $fs->float, | |
| 'c3' => $fs->float, | |
| 'c4' => $fs->float | |
| ]; | |
| } else { | |
| return [ | |
| 'type' => $type | |
| ]; | |
| } | |
| } | |
| function readSkin($fs, $name, &$skel) { | |
| $skin = []; | |
| $skin['name'] = $name; | |
| $slotCount = readVarInt32($fs); | |
| for ($i=0; $i<$slotCount; $i++) { | |
| $slot = []; | |
| $slot['index'] = readVarInt32($fs); | |
| $attachmentCount = readVarInt32($fs); | |
| $slot['attachment'] = []; | |
| for ($j=0; $j<$attachmentCount; $j++) { | |
| $attachment = []; | |
| $attachment['placeholderName'] = readString($fs); | |
| $attachment['name'] = readString($fs); | |
| if ($attachment['name'] === NULL) $attachment['name'] = $attachment['placeholderName']; | |
| $attachment['type'] = ord($fs->byte); | |
| switch($attachment['type']) { | |
| case ATTACHMENT_REGION: { | |
| $attachment['path'] = readString($fs); | |
| $attachment['rotation'] = $fs->float; | |
| $attachment['x'] = $fs->float; | |
| $attachment['y'] = $fs->float; | |
| $attachment['scaleX'] = $fs->float; | |
| $attachment['scaleY'] = $fs->float; | |
| $attachment['width'] = $fs->float; | |
| $attachment['height'] = $fs->float; | |
| $attachment['color'] = readColor($fs); | |
| break; | |
| } | |
| case ATTACHMENT_BOUNDING_BOX: { | |
| $vertexCount = readVarInt32($fs); | |
| $attachment['vertexCount'] = $vertexCount; | |
| list($attachment['vertices'], $attachment['weighted']) = readVertex($fs, $vertexCount, true); | |
| if ($skel['nonessential']) $attachment['color'] = readColor($fs); | |
| break; | |
| } | |
| case ATTACHMENT_MESH: { | |
| $attachment['path'] = readString($fs); | |
| $attachment['color'] = readColor($fs); | |
| $uvCount = readVarInt32($fs); | |
| $attachment['uvs'] = []; | |
| for ($k=0; $k<$uvCount; $k++) { | |
| $attachment['uvs'][] = $fs->float; | |
| $attachment['uvs'][] = $fs->float; | |
| } | |
| $triangleCount = readVarInt32($fs); | |
| $attachment['triangles'] = []; | |
| for ($k=0; $k<$triangleCount; $k++) { | |
| $attachment['triangles'][] = $fs->short; | |
| } | |
| list($attachment['vertices'], $attachment['weighted']) = readVertex($fs, $uvCount, true); | |
| $attachment['hull'] = readVarInt32($fs); | |
| if ($skel['nonessential']) { | |
| $edgeCount = readVarInt32($fs); | |
| $attachment['edges'] = []; | |
| for ($k=0; $k<$edgeCount; $k++) { | |
| $attachment['edges'][] = $fs->short; | |
| } | |
| $attachment['width'] = $fs->float; | |
| $attachment['height'] = $fs->float; | |
| } | |
| break; | |
| } | |
| case ATTACHMENT_LINKED_MESH: { | |
| $attachment['path'] = readString($fs); | |
| $attachment['color'] = readColor($fs); | |
| $attachment['skin'] = readString($fs); | |
| $attachment['parent'] = readString($fs); | |
| $attachment['deform'] = $fs->bool; | |
| if ($skel['nonessential']) { | |
| $attachment['width'] = $fs->float; | |
| $attachment['height'] = $fs->float; | |
| } | |
| break; | |
| } | |
| case ATTACHMENT_PATH: { | |
| $attachment['closed'] = $fs->bool; | |
| $attachment['constantSpeed'] = $fs->bool; | |
| $vertexCount = readVarInt32($fs); | |
| list($attachment['vertices'], $attachment['weighted']) = readVertex($fs, $vertexCount, true); | |
| $attachment['lengths'] = []; | |
| for ($k=0; $k<$vertexCount/3; $k++) { | |
| $attachment['lengths'][] = $fs->float; | |
| } | |
| if ($skel['nonessential']) $attachment['color'] = readColor($fs); | |
| break; | |
| } | |
| case ATTACHMENT_POINT: { | |
| $attachment['rotation'] = $fs->float; | |
| $attachment['x'] = $fs->float; | |
| $attachment['y'] = $fs->float; | |
| if ($skel['nonessential']) $attachment['color'] = readColor($fs); | |
| break; | |
| } | |
| case ATTACHMENT_CLIPPING: { | |
| $attachment['endSlot'] = readVarInt32($fs); | |
| $vertexCount = readVarInt32($fs); | |
| $attachment['vertexCount'] = $vertexCount; | |
| list($attachment['vertices'], $attachment['weighted']) = readVertex($fs, $vertexCount, true); | |
| if ($skel['nonessential']) $attachment['color'] = readColor($fs); | |
| break; | |
| } | |
| } | |
| $slot['attachment'][] = $attachment; | |
| } | |
| $skin[] = $slot; | |
| } | |
| return $skin; | |
| } | |
| function readVertex($fs, $count, $isSkin = false) { | |
| $weighted = $fs->bool; | |
| $data = []; | |
| for ($i=0; $i<$count; $i++) { | |
| if (!$weighted) { | |
| $data[] = $fs->float; | |
| $data[] = $fs->float; | |
| } else { | |
| $boneCount = readVarInt32($fs); | |
| $isSkin && ($data[] = $boneCount); | |
| for ($j=0, $bones=[]; $j<$boneCount; $j++) { | |
| $index = readVarInt32($fs); | |
| $x = $fs->float; | |
| $y = $fs->float; | |
| $weight = $fs->float; | |
| if ($isSkin) { | |
| $data[] = $index; | |
| $data[] = $x; | |
| $data[] = $y; | |
| $data[] = $weight; | |
| } else { | |
| $data[] = $weight; | |
| $data[] = $index; | |
| $data[] = $x; | |
| $data[] = $y; | |
| } | |
| } | |
| } | |
| } | |
| return [$data,$weighted]; | |
| } | |
| function readBone($fs, $nonessential, $isFirst) { | |
| $bone = []; | |
| $bone['name'] = readString($fs); | |
| if ($isFirst) $bone['parent'] = readVarInt32($fs); | |
| $bone['rotation'] = $fs->float; | |
| $bone['x'] = $fs->float; | |
| $bone['y'] = $fs->float; | |
| $bone['scaleX'] = $fs->float; | |
| $bone['scaleY'] = $fs->float; | |
| $bone['shearX'] = $fs->float; | |
| $bone['shearY'] = $fs->float; | |
| $bone['length'] = $fs->float; | |
| $bone['transformMode'] = ord($fs->byte); | |
| if ($nonessential) $bone['color'] = readColor($fs); | |
| return $bone; | |
| } | |
| function readSlot($fs) { | |
| $slot = []; | |
| $slot['name'] = readString($fs); | |
| $slot['bone'] = readVarInt32($fs); | |
| $slot['color'] = bin2hex($fs->readData(4)); | |
| if ($slot['color'] == 'ffffffff') unset($slot['color']); | |
| $slot['dark'] = bin2hex($fs->readData(4)); | |
| if ($slot['dark'] == 'ffffffff') unset($slot['dark']); | |
| $slot['attachment'] = readString($fs); | |
| $slot['blend'] = ord($fs->byte); | |
| return $slot; | |
| } | |
| function GetSkinAttachment(&$skin, $slotIndex, $attachmentName) { | |
| $slot = &$skin[$slotIndex]; | |
| for ($i=0, $n=count($slot['attachment']); $i<$n; $i++) { | |
| if ($slot['attachment'][$i]['placeholderName'] == $attachmentName) return $slot['attachment'][$i]; | |
| } | |
| return NULL; | |
| } | |
| function readAnimation($fs, &$skel) { | |
| $animation = []; | |
| $animation['name'] = readString($fs); | |
| $slotCount = readVarInt32($fs); | |
| $animation['slot'] = []; | |
| for ($j=0; $j<$slotCount; $j++) { | |
| $slot = []; | |
| $slot['index'] = readVarInt32($fs); | |
| $timelineCount = readVarInt32($fs); | |
| $slot['timelineCount'] = $timelineCount; | |
| $slot['timeline'] = []; | |
| for ($k=0; $k<$timelineCount; $k++) { | |
| $timeline = []; | |
| $timeline['type'] = ord($fs->byte); | |
| $frameCount = readVarInt32($fs); | |
| $timeline['frame'] = []; | |
| for ($l=0; $l<$frameCount; $l++) { | |
| switch ($timeline['type']) { | |
| case SLOT_ATTACHMENT: { | |
| $frame = [ | |
| 'time' => $fs->float, | |
| 'name' => readString($fs) | |
| ]; | |
| $timeline['frame'][] = $frame; | |
| break; | |
| } | |
| case SLOT_COLOR: { | |
| $frame = [ | |
| 'time' => $fs->float, | |
| 'color' => readColor($fs) | |
| ]; | |
| if ($l < $frameCount - 1) $frame['curve'] = readCurve($fs); | |
| $timeline['frame'][] = $frame; | |
| break; | |
| } | |
| case SLOT_TWO_COLOR: { | |
| $frame = [ | |
| 'time' => $fs->float, | |
| 'light' => readColor($fs), | |
| 'dark' => readColor($fs) | |
| ]; | |
| if ($l < $frameCount - 1) $frame['curve'] = readCurve($fs); | |
| $timeline['frame'][] = $frame; | |
| break; | |
| } | |
| } | |
| } | |
| $slot['timeline'][] = $timeline; | |
| } | |
| $animation['slot'][] = $slot; | |
| } | |
| $boneCount = readVarInt32($fs); | |
| $animation['bone'] = []; | |
| for ($j=0; $j<$boneCount; $j++) { | |
| $bone = []; | |
| $bone['index'] = readVarInt32($fs); | |
| $timelineCount = readVarInt32($fs); | |
| $bone['timelineCount'] = $timelineCount; | |
| $bone['timeline'] = []; | |
| for ($k=0; $k<$timelineCount; $k++) { | |
| $timeline = []; | |
| $timeline['type'] = ord($fs->byte); | |
| $frameCount = readVarInt32($fs); | |
| $timeline['frame'] = []; | |
| for ($l=0; $l<$frameCount; $l++) { | |
| switch ($timeline['type']) { | |
| case BONE_ROTATE: { | |
| $frame = [ | |
| 'time' => $fs->float, | |
| 'rotation' => $fs->float | |
| ]; | |
| if ($l < $frameCount - 1) $frame['curve'] = readCurve($fs); | |
| $timeline['frame'][] = $frame; | |
| break; | |
| } | |
| case BONE_TRANSLATE: case BONE_SCALE: case BONE_SHEAR: { | |
| $frame = [ | |
| 'time' => $fs->float, | |
| 'x' => $fs->float, | |
| 'y' => $fs->float | |
| ]; | |
| if ($l < $frameCount - 1) $frame['curve'] = readCurve($fs); | |
| $timeline['frame'][] = $frame; | |
| break; | |
| } | |
| } | |
| } | |
| $bone['timeline'][] = $timeline; | |
| } | |
| $animation['bone'][] = $bone; | |
| } | |
| $ikCount = readVarInt32($fs); | |
| if ($ikCount) { | |
| $animation['ik'] = []; | |
| for ($i=0; $i<$ikCount; $i++) { | |
| $ik = []; | |
| $ik['index'] = readVarInt32($fs); | |
| $ik['frame'] = []; | |
| for ($ii=0, $nn=readVarInt32($fs); $ii<$nn; $ii++) { | |
| $frame = [ | |
| 'time' => $fs->float, | |
| 'mix' => $fs->float, | |
| 'bendPositive' => $fs->bool | |
| ]; | |
| if ($ii < $nn - 1) $frame['curve'] = readCurve($fs); | |
| $ik['frame'][] = $frame; | |
| } | |
| $animation['ik'][] = $ik; | |
| } | |
| } | |
| $transformCount = readVarInt32($fs); | |
| if ($transformCount) { | |
| $animation['transform'] = []; | |
| for ($i=0; $i<$transformCount; $i++) { | |
| $transform = []; | |
| $transform['index'] = readVarInt32($fs); | |
| $transform['frame'] = []; | |
| for ($ii=0, $nn=readVarInt32($fs); $ii<$nn; $ii++) { | |
| $frame = [ | |
| 'time' => $fs->float, | |
| 'rotateMix' => $fs->float, | |
| 'translateMix' => $fs->float, | |
| 'scaleMix' => $fs->float, | |
| 'shearMix' => $fs->float | |
| ]; | |
| if ($l < $frameCount - 1) $frame['curve'] = readCurve($fs); | |
| $transform['frame'][] = $frame; | |
| } | |
| $animation['transform'][] = $transform; | |
| } | |
| } | |
| $pathCount = readVarInt32($fs); | |
| if ($pathCount) { | |
| throw new Exception('has path timeline data'); | |
| } | |
| $deformCount = readVarInt32($fs); | |
| if ($deformCount) { | |
| $animation['deform'] = []; | |
| for ($i=0; $i<$deformCount; $i++) { | |
| $deform=[]; | |
| $deform['skin'] = readVarInt32($fs); | |
| $skin = &$skel['skin'][$deform['skin']]; | |
| $deform['slots'] = []; | |
| for ($ii=0, $nn=readVarInt32($fs); $ii<$nn; $ii++) { | |
| $slot = []; | |
| $slot['index'] = readVarInt32($fs); | |
| $slot['mesh'] = []; | |
| for ($iii=0, $nnn=readVarInt32($fs); $iii<$nnn; $iii++) { | |
| $mesh = []; | |
| $mesh['attachment'] = readString($fs); | |
| $attachment = GetSkinAttachment($skin, $slot['index'], $mesh['attachment']); | |
| $weighted = $attachment['weighted']; | |
| $vertices = $attachment['vertices']; | |
| $deformLength = $weighted ? count($vertices) /3*2 : count($vertices); | |
| $frameCount = readVarInt32($fs); | |
| $mesh['frame'] = []; | |
| for ($iiii=0; $iiii<$frameCount; $iiii++) { | |
| $frame = [ | |
| 'time' => $fs->float, | |
| 'end' => readVarInt32($fs) | |
| ]; | |
| if ($frame['end'] == 0) { | |
| $frame['vertices'] = $weighted ? array_fill(0, $deformLength, 0) : $vertices; | |
| } else { | |
| $deforms = array_fill(0, $deformLength, 0); | |
| $start = readVarInt32($fs); | |
| $end = $frame['end']; | |
| $end += $start; | |
| // scale??? | |
| for ($v=$start; $v<$end; $v++) { | |
| $deforms[$v] = $fs->float; | |
| } | |
| if (!$weighted) { | |
| for ($v=0; $v<$deformLength; $v++) { | |
| $deforms[$v] += $vertices[$v]; | |
| } | |
| } | |
| $frame['vertices'] = $deforms; | |
| } | |
| $v = count($frame['vertices']) - 1; | |
| while ($v>=0 && $frame['vertices'][$v] == 0) { | |
| unset($frame['vertices'][$v--]); | |
| } | |
| if ($iiii < $frameCount - 1) $frame['curve'] = readCurve($fs); | |
| $mesh['frame'][] = $frame; | |
| } | |
| $slot['mesh'][] = $mesh; | |
| } | |
| $deform['slots'][] = $slot; | |
| } | |
| $animation['deform'][] = $deform; | |
| } | |
| } | |
| $drawOrderCount = readVarInt32($fs); | |
| if ($drawOrderCount) { | |
| $animation['drawOrder'] = []; | |
| for ($j=0; $j<$drawOrderCount; $j++) { | |
| $drawOrder = [ | |
| 'time' => $fs->float, | |
| 'change' => [] | |
| ]; | |
| $changeCount = readVarInt32($fs); | |
| for ($k=0; $k<$changeCount; $k++) { | |
| $drawOrder['change'][] = [ | |
| 'index' => readVarInt32($fs), | |
| 'amount' => readVarInt32($fs) | |
| ]; | |
| } | |
| $animation['drawOrder'][] = $drawOrder; | |
| } | |
| } | |
| $eventCount = readVarInt32($fs); | |
| if ($eventCount) { | |
| $animation['event'] = []; | |
| for ($j=0; $j<$eventCount; $j++) { | |
| $event = [ | |
| 'time' => $fs->float, | |
| 'index' => readVarInt32($fs), | |
| 'int' => readVarInt32($fs, false), | |
| 'float' => $fs->float | |
| ]; | |
| $hasString = $fs->bool; | |
| if ($hasString) $event['string'] = readString($fs); | |
| $animation['event'][] = $event; | |
| } | |
| } | |
| return $animation; | |
| } | |
| { | |
| // define constant | |
| define('ATTACHMENT_REGION', 0); | |
| define('ATTACHMENT_BOUNDING_BOX', 1); | |
| define('ATTACHMENT_MESH', 2); | |
| define('ATTACHMENT_LINKED_MESH', 3); | |
| define('ATTACHMENT_PATH', 4); | |
| define('ATTACHMENT_POINT', 5); | |
| define('ATTACHMENT_CLIPPING', 6); | |
| define('BLEND_MODE_NORMAL', 0); | |
| define('BLEND_MODE_ADDITIVE', 1); | |
| define('BLEND_MODE_MULTIPLY', 2); | |
| define('BLEND_MODE_SCREEN', 3); | |
| define('CURVE_LINEAR', 0); | |
| define('CURVE_STEPPED', 1); | |
| define('CURVE_BEZIER', 2); | |
| define('BONE_ROTATE', 0); | |
| define('BONE_TRANSLATE', 1); | |
| define('BONE_SCALE', 2); | |
| define('BONE_SHEAR', 3); | |
| define('TRANSFORM_NORMAL', 0); | |
| define('TRANSFORM_ONLY_TRANSLATION', 1); | |
| define('TRANSFORM_NO_ROTATION_OR_REFLECTION', 2); | |
| define('TRANSFORM_NO_SCALE', 3); | |
| define('TRANSFORM_NO_SCALE_OR_REFLECTION', 4); | |
| define('SLOT_ATTACHMENT', 0); | |
| define('SLOT_COLOR', 1); | |
| define('SLOT_TWO_COLOR', 2); | |
| define('PATH_POSITION', 0); | |
| define('PATH_SPACING', 1); | |
| define('PATH_MIX', 2); | |
| define('PATH_POSITION_FIXED', 0); | |
| define('PATH_POSITION_PERCENT', 1); | |
| define('PATH_SPACING_LENGTH', 0); | |
| define('PATH_SPACING_FIXED', 1); | |
| define('PATH_SPACING_PERCENT', 2); | |
| define('PATH_ROTATE_TANGENT', 0); | |
| define('PATH_ROTATE_CHAIN', 1); | |
| define('PATH_ROTATE_CHAIN_SCALE', 2); | |
| } | |
| function exportSkel($file) { | |
| $fs = new FileStream($file); | |
| $out = pathinfo($file, PATHINFO_DIRNAME).'/'.pathinfo($file, PATHINFO_FILENAME).'.json'; | |
| // read binary. some not implemented | |
| $skel = ['skeleton'=>[]]; | |
| $skel['skeleton']['hash'] = readString($fs); | |
| $skel['skeleton']['version'] = readString($fs); | |
| $skel['skeleton']['width'] = $fs->float; | |
| $skel['skeleton']['height'] = $fs->float; | |
| $skel['nonessential'] = $fs->bool; | |
| $nonessential = $skel['nonessential']; | |
| if ($nonessential) { | |
| $skel['skeleton']['fps'] = $fs->float; | |
| $skel['skeleton']['images'] = readString($fs); | |
| } | |
| $boneCount = readVarInt32($fs); | |
| if ($boneCount) { | |
| $skel['bone'] = []; | |
| for ($i=0; $i<$boneCount; $i++) { | |
| $skel['bone'][] = readBone($fs, $nonessential, $i!=0); | |
| } | |
| } | |
| $slotCount = readVarInt32($fs); | |
| if ($slotCount) { | |
| $skel['slot'] = []; | |
| for ($i=0; $i<$slotCount; $i++) { | |
| $skel['slot'][] = readSlot($fs); | |
| } | |
| } | |
| $ikCount = readVarInt32($fs); | |
| if ($ikCount) { | |
| $skel['ik'] = []; | |
| for ($i=0; $i<$ikCount; $i++) { | |
| $ik = []; | |
| $ik['name'] = readString($fs); | |
| $ik['order'] = readVarInt32($fs); | |
| $boneCount = readVarInt32($fs); | |
| $ik['bone'] = []; | |
| for ($ii=0; $ii<$boneCount; $ii++) { | |
| $ik['bone'][] = readVarInt32($fs); | |
| } | |
| $ik['target'] = readVarInt32($fs); | |
| $ik['mix'] = $fs->float; | |
| $ik['bendPositive'] = $fs->bool; | |
| $skel['ik'][] = $ik; | |
| } | |
| } | |
| $transformCount = readVarInt32($fs); | |
| if ($transformCount) { | |
| $skel['transform'] = []; | |
| for ($i=0; $i<$transformCount; $i++) { | |
| $next = readString($fs); | |
| $next = readVarInt32($fs); | |
| $len = readVarInt32($fs); | |
| for ($k=0; $k<$len; $k++) $next = readVarInt32($fs); | |
| $next = readVarInt32($fs); | |
| $next = $fs->bool; | |
| $next = $fs->bool; | |
| $next = $fs->float; | |
| $next = $fs->float; | |
| $next = $fs->float; | |
| $next = $fs->float; | |
| $next = $fs->float; | |
| $next = $fs->float; | |
| $next = $fs->float; | |
| $next = $fs->float; | |
| $next = $fs->float; | |
| $next = $fs->float; | |
| } | |
| throw new Exception('has transform data'); | |
| } | |
| $pathCount = readVarInt32($fs); | |
| if ($pathCount) { | |
| $skel['path'] = []; | |
| throw new Exception('has path data'); | |
| } | |
| $skel['skin'] = [ | |
| readSkin($fs, 'default', $skel) | |
| ]; | |
| $skinCount = readVarInt32($fs); | |
| for ($i=0; $i<$skinCount; $i++) { | |
| $name = readString($fs); | |
| $skel['skin'][] = readSkin($fs, $name, $skel); | |
| } | |
| $eventCount = readVarInt32($fs); | |
| if ($eventCount) { | |
| $skel['event'] = []; | |
| for ($i=0; $i<$eventCount; $i++) { | |
| $skel['event'][] = [ | |
| 'name' => readString($fs), | |
| 'int' => readVarInt32($fs, false), | |
| 'float' => $fs->float, | |
| 'string' => readString($fs) | |
| ]; | |
| } | |
| } | |
| $animationCount = readVarInt32($fs); | |
| if ($animationCount) { | |
| $skel['animation'] = []; | |
| for ($i=0; $i<$animationCount; $i++) { | |
| $skel['animation'][] = readAnimation($fs, $skel); | |
| } | |
| } | |
| $json = processToJson($skel); | |
| $i=0; | |
| foreach ($json['skins']['default'] as $k=>&$v) { | |
| //echo $k."\n"; | |
| //if (++$i<35)unset($json['skins']['default'][$k]); | |
| //else if (in_array($k, ['hair_1', 'hair_3']))unset($json['animations'][$k]); | |
| } | |
| //unset($json['animations']); | |
| //unset($json['skins']); | |
| file_put_contents($out, json_encode($json)); | |
| } | |
| function processToJson($skel) { | |
| // process to json | |
| $json = [ | |
| 'skeleton' => $skel['skeleton'] | |
| ]; | |
| if (isset($skel['bone'])) { | |
| $json['bones'] = array_map(function ($i) use(&$json) { | |
| // unset default 0 prop | |
| foreach (['x','y','shearX','shearY','length'] as $prop) { | |
| if ($i[$prop] == 0) unset($i[$prop]); | |
| } | |
| // unset default 1 prop | |
| foreach (['scaleX','scaleY'] as $prop) { | |
| if ($i[$prop] == 1) unset($i[$prop]); | |
| } | |
| if (isset($i['transformMode'])) { | |
| $i['transform'] = [ | |
| TRANSFORM_NORMAL => 'normal', | |
| TRANSFORM_ONLY_TRANSLATION => 'onlyTranslation', | |
| TRANSFORM_NO_ROTATION_OR_REFLECTION => 'noRotationOrReflection', | |
| TRANSFORM_NO_SCALE => 'noScale', | |
| TRANSFORM_NO_SCALE_OR_REFLECTION => 'noScaleOrReflection' | |
| ][$i['transformMode']]; | |
| unset($i['transformMode']); | |
| if ($i['transform'] == 'normal') unset($i['transform']); | |
| } | |
| return $i; | |
| }, $skel['bone']); | |
| foreach ($json['bones'] as &$i) { | |
| if (isset($i['parent'])) { | |
| $i['parent'] = $json['bones'][ $i['parent'] ]['name']; | |
| } | |
| } | |
| } | |
| if (isset($skel['slot'])) { | |
| $json['slots'] = array_map(function ($i) use(&$json) { | |
| $i['bone'] = $json['bones'][$i['bone']]['name']; | |
| if ($i['attachment'] === NULL) unset($i['attachment']); | |
| $i['blend'] = [ | |
| BLEND_MODE_NORMAL => 'normal', | |
| BLEND_MODE_ADDITIVE => 'additive', | |
| BLEND_MODE_MULTIPLY => 'multiply', | |
| BLEND_MODE_SCREEN => 'screen' | |
| ][$i['blend']]; | |
| if ($i['blend'] == 'normal') unset($i['blend']); | |
| return $i; | |
| }, $skel['slot']); | |
| } | |
| if (isset($skel['ik'])) { | |
| $json['ik'] = array_map(function ($ik) use(&$json) { | |
| $ik = [ | |
| 'name' => $ik['name'], | |
| 'order' => $ik['order'], | |
| 'bones' => array_map(function ($i)use(&$json) { return $json['bones'][ $i ]['name']; }, $ik['bone']), | |
| 'target' => $json['bones'][ $ik['target'] ]['name'], | |
| 'mix' => $ik['mix'], | |
| 'bendPositive' => $ik['bendPositive'] | |
| ]; | |
| if ($ik['mix'] == 1) unset($ik['mix']); | |
| if ($ik['bendPositive'] === false) unset($ik['bendPositive']); | |
| return $ik; | |
| }, $skel['ik']); | |
| } | |
| // transform path | |
| if (isset($skel['skin'])) { | |
| $json['skins'] = []; | |
| foreach ($skel['skin'] as $skin) { | |
| $procSkin = []; | |
| $skinname = $skin['name']; | |
| unset($skin['name']); | |
| foreach ($skin as $slot) { | |
| $procSlot = []; | |
| foreach ($slot['attachment'] as $attachment) { | |
| // unset default 0 prop | |
| foreach (['x','y','rotation'] as $prop) { | |
| if (isset($attachment[$prop]) && $attachment[$prop] == 0) unset($attachment[$prop]); | |
| } | |
| // unset default 1 prop | |
| foreach (['scaleX','scaleY'] as $prop) { | |
| if (isset($attachment[$prop]) && $attachment[$prop] == 1) unset($attachment[$prop]); | |
| } | |
| $attachment['type'] = [ | |
| ATTACHMENT_REGION => 'region', | |
| ATTACHMENT_BOUNDING_BOX => 'boundingbox', | |
| ATTACHMENT_MESH => 'mesh', | |
| ATTACHMENT_LINKED_MESH => 'linkedmesh', | |
| ATTACHMENT_PATH => 'path', | |
| ATTACHMENT_POINT => 'point', | |
| ATTACHMENT_CLIPPING => 'clipping' | |
| ][$attachment['type']]; | |
| if (in_array($attachment['type'], ['region','mesh','linkedmesh']) && $attachment['path'] === null) unset($attachment['path']); | |
| if (isset($attachment['color']) && $attachment['color'] === 'FFFFFFFF') unset($attachment['color']); | |
| if ($attachment['type'] == 'clipping') { | |
| $attachment['end'] = $json['slots'][ $attachment['endSlot'] ]['name']; | |
| unset($attachment['endSlot']); | |
| } | |
| $procSlot[ $attachment['placeholderName'] ] = $attachment; | |
| } | |
| $procSkin[ $json['slots'][ $slot['index'] ]['name'] ] = $procSlot; | |
| } | |
| $json['skins'][$skinname] = $procSkin; | |
| } | |
| } | |
| // event | |
| if (isset($skel['animation'])) { | |
| $json['animations'] = []; | |
| foreach ($skel['animation'] as $animname=>$anim) { | |
| $procAnim = []; | |
| if (isset($anim['bone'])) { | |
| $procAnim['bones'] = []; | |
| foreach ($anim['bone'] as $bone) { | |
| $procBone = []; | |
| foreach ($bone['timeline'] as $timeline) { | |
| $procFrame = []; | |
| $frameType = [ | |
| BONE_ROTATE => 'rotate', | |
| BONE_TRANSLATE => 'translate', | |
| BONE_SCALE => 'scale', | |
| BONE_SHEAR => 'shear' | |
| ][$timeline['type']]; | |
| foreach ($timeline['frame'] as $frame) { | |
| if (isset($frame['rotation'])) { | |
| $frame['angle'] = $frame['rotation']; | |
| unset($frame['rotation']); | |
| } | |
| if (isset($frame['curve'])) { | |
| if ($frame['curve']['type'] == CURVE_BEZIER) { | |
| $frame['curve'] = [ | |
| $frame['curve']['c1'], | |
| $frame['curve']['c2'], | |
| $frame['curve']['c3'], | |
| $frame['curve']['c4'] | |
| ]; | |
| } else if ($frame['curve']['type'] == CURVE_STEPPED) { | |
| $frame['curve'] = 'stepped'; | |
| } else { | |
| unset($frame['curve']); | |
| } | |
| } | |
| $procFrame[] = $frame; | |
| } | |
| $procBone[$frameType] = $procFrame; | |
| } | |
| $procAnim['bones'][ $json['bones'][ $bone['index'] ]['name'] ] = $procBone; | |
| } | |
| } | |
| if (isset($anim['slot'])) { | |
| $procAnim['slots'] = []; | |
| foreach ($anim['slot'] as $slot) { | |
| $procSlot = []; | |
| foreach ($slot['timeline'] as $timeline) { | |
| $procFrame = []; | |
| $frameType = [ | |
| SLOT_ATTACHMENT => 'attachment', | |
| SLOT_COLOR => 'color', | |
| SLOT_TWO_COLOR => 'two_color' | |
| ][$timeline['type']]; | |
| foreach ($timeline['frame'] as $frame) { | |
| if (isset($frame['rotation'])) { | |
| $frame['angle'] = $frame['rotation']; | |
| unset($frame['rotation']); | |
| } | |
| if (isset($frame['curve'])) { | |
| if ($frame['curve']['type'] == CURVE_BEZIER) { | |
| $frame['curve'] = [ | |
| $frame['curve']['c1'], | |
| $frame['curve']['c2'], | |
| $frame['curve']['c3'], | |
| $frame['curve']['c4'] | |
| ]; | |
| } else if ($frame['curve']['type'] == CURVE_STEPPED) { | |
| $frame['curve'] = 'stepped'; | |
| } else { | |
| unset($frame['curve']); | |
| } | |
| } | |
| $procFrame[] = $frame; | |
| } | |
| $procSlot[$frameType] = $procFrame; | |
| } | |
| $procAnim['slots'][ $json['slots'][ $slot['index'] ]['name'] ] = $procSlot; | |
| } | |
| } | |
| if (isset($anim['ik'])) { | |
| $procAnim['ik'] = []; | |
| foreach ($anim['ik'] as $ik) { | |
| $procAnim['ik'][ $skel['ik'][ $ik['index'] ]['name'] ] = array_map(function ($f) { | |
| if ($f['mix'] == 1) unset($f['mix']); | |
| if ($f['bendPositive'] === false) unset($f['bendPositive']); | |
| if (isset($f['curve'])) { | |
| if ($f['curve']['type'] == CURVE_BEZIER) { | |
| $f['curve'] = [ | |
| $f['curve']['c1'], | |
| $f['curve']['c2'], | |
| $f['curve']['c3'], | |
| $f['curve']['c4'] | |
| ]; | |
| } else if ($f['curve']['type'] == CURVE_STEPPED) { | |
| $f['curve'] = 'stepped'; | |
| } else { | |
| unset($f['curve']); | |
| } | |
| } | |
| return $f; | |
| }, $ik['frame']); | |
| } | |
| } | |
| if (isset($anim['transform'])) { | |
| /*$procAnim['transform'] = []; | |
| foreach ($anim['transform'] as $transform) { | |
| $procAnim['transform'][ $skel['transform'][ $transform['index'] ]['name'] ] = array_map(function ($f) { | |
| if ($f['rotateMix'] == 1) unset($f['rotateMix']); | |
| if ($f['translateMix'] == 1) unset($f['translateMix']); | |
| if ($f['scaleMix'] == 1) unset($f['scaleMix']); | |
| if ($f['shearMix'] == 1) unset($f['shearMix']); | |
| return $f; | |
| }, $transform['frame']); | |
| }*/ | |
| } | |
| if (isset($anim['deform'])) { | |
| $procAnim['deform'] = []; | |
| foreach ($anim['deform'] as $skin) { | |
| $procSkin = []; | |
| foreach ($skin['slots'] as $slot) { | |
| $procSlot = []; | |
| foreach ($slot['mesh'] as $mesh) { | |
| $procMesh = []; | |
| foreach ($mesh['frame'] as $frame) { | |
| unset($frame['end']); | |
| if (isset($frame['curve'])) { | |
| if ($frame['curve']['type'] == CURVE_BEZIER) { | |
| $frame['curve'] = [ | |
| $frame['curve']['c1'], | |
| $frame['curve']['c2'], | |
| $frame['curve']['c3'], | |
| $frame['curve']['c4'] | |
| ]; | |
| } else if ($frame['curve']['type'] == CURVE_STEPPED) { | |
| $frame['curve'] = 'stepped'; | |
| } else { | |
| unset($frame['curve']); | |
| } | |
| } | |
| $procMesh[] = $frame; | |
| } | |
| $procSlot[$mesh['attachment']] = $procMesh; | |
| } | |
| $procSkin[ $json['slots'][ $slot['index'] ]['name'] ] = $procSlot; | |
| } | |
| $procAnim['deform'][ $skel['skin'][ $skin['skin'] ]['name'] ] = $procSkin; | |
| } | |
| } | |
| // event | |
| if (isset($anim['drawOrder'])) { | |
| $procAnim['draworder'] = []; | |
| foreach ($anim['drawOrder'] as $drawOrder) { | |
| $procdraw = []; | |
| $procdraw['time'] = $drawOrder['time']; | |
| $procdraw['offsets'] = array_map(function ($i) use (&$json) { | |
| return [ | |
| 'slot' => $json['slots'][ $i['index'] ]['name'], | |
| 'offset' => $i['amount'] | |
| ]; | |
| }, $drawOrder['change']); | |
| $procAnim['draworder'][] = $procdraw; | |
| } | |
| } | |
| $json['animations'][ $anim['name'] ] = $procAnim; | |
| } | |
| } | |
| return $json; | |
| } | |
| function readCyspSkeleton($skelFile) { | |
| $fs = new FileStream($skelFile); | |
| // check file format | |
| $fs->littleEndian = true; | |
| if (!( | |
| $fs->readData(4) == 'cysp' && | |
| $fs->long === 0 && | |
| $fs->long === 1 | |
| )) { | |
| throw new Exception('invalid cysp format'); | |
| } | |
| $bodyCount = $fs->long; | |
| $fs->littleEndian = false; | |
| $fs->position = ($bodyCount + 1) * 32; | |
| // read binary. some not implemented | |
| $skel = ['skeleton'=>[]]; | |
| $skel['skeleton']['hash'] = readString($fs); | |
| $skel['skeleton']['version'] = readString($fs); | |
| $skel['skeleton']['width'] = $fs->float; | |
| $skel['skeleton']['height'] = $fs->float; | |
| $skel['nonessential'] = $fs->bool; | |
| $nonessential = $skel['nonessential']; | |
| if ($nonessential) { | |
| $skel['skeleton']['fps'] = $fs->float; | |
| $skel['skeleton']['images'] = readString($fs); | |
| } | |
| $boneCount = readVarInt32($fs); | |
| if ($boneCount) { | |
| $skel['bone'] = []; | |
| for ($i=0; $i<$boneCount; $i++) { | |
| $skel['bone'][] = readBone($fs, $nonessential, $i!=0); | |
| } | |
| } | |
| $slotCount = readVarInt32($fs); | |
| if ($slotCount) { | |
| $skel['slot'] = []; | |
| for ($i=0; $i<$slotCount; $i++) { | |
| $skel['slot'][] = readSlot($fs); | |
| } | |
| } | |
| $ikCount = readVarInt32($fs); | |
| if ($ikCount) { | |
| $skel['ik'] = []; | |
| throw new Exception('has ik data'); | |
| } | |
| $transformCount = readVarInt32($fs); | |
| if ($transformCount) { | |
| $skel['transform'] = []; | |
| throw new Exception('has transform data'); | |
| } | |
| $pathCount = readVarInt32($fs); | |
| if ($pathCount) { | |
| $skel['path'] = []; | |
| throw new Exception('has path data'); | |
| } | |
| $skel['skin'] = [ | |
| readSkin($fs, 'default', $skel) | |
| ]; | |
| $skinCount = readVarInt32($fs); | |
| for ($i=0; $i<$skinCount; $i++) { | |
| $name = readString($fs); | |
| $skel['skin'][] = readSkin($fs, $name, $skel); | |
| } | |
| $eventCount = readVarInt32($fs); | |
| if ($eventCount) { | |
| $skel['event'] = []; | |
| for ($i=0; $i<$eventCount; $i++) { | |
| $skel['event'][] = [ | |
| 'name' => readString($fs), | |
| 'int' => readVarInt32($fs, false), | |
| 'float' => $fs->float, | |
| 'string' => readString($fs) | |
| ]; | |
| } | |
| } | |
| return $skel; | |
| $json = processToJson($skel); | |
| file_put_contents($out, json_encode($json['animations'], JSON_UNESCAPED_SLASHES)); | |
| //file_put_contents($out, json_encode($json, JSON_UNESCAPED_SLASHES)); | |
| } | |
| function readCyspAnimation($animFile, $skel) { | |
| $animFs = new FileStream($animFile); | |
| $animation = []; | |
| $animFs->littleEndian = true; | |
| if (!( | |
| $animFs->readData(4) == 'cysp' && | |
| $animFs->long === 0 && | |
| $animFs->long === 1 | |
| )) { | |
| throw new Exception('invalid cysp format'); | |
| } | |
| $animationCount = $animFs->long; | |
| $animFs->littleEndian = false; | |
| $animFs->position = ($animationCount + 1) * 32; | |
| if ($animationCount) { | |
| for ($i=0; $i<$animationCount; $i++) { | |
| $animation[] = readAnimation($animFs, $skel); | |
| } | |
| } | |
| return $animation; | |
| } | |
| if (defined('TEST_SUITE') && TEST_SUITE == __FILE__) { | |
| //exportSkel('106311.skel'); | |
| //exportSkel('106331.skel'); | |
| chdir(__DIR__); | |
| //echo "muse/1_rock_main_show.skel\n"; | |
| exportSkel('muse/1_rock_main_show.skel'); | |
| //echo "muse/1_rock_victory.skel\n"; | |
| //exportSkel('muse/1_rock_victory.skel'); | |
| //echo "muse/char_1_rock.skel\n"; | |
| //exportSkel('muse/char_1_rock.skel'); | |
| exit; | |
| //exportSkel('alien-pro.skel'); | |
| //exportCyspSkel('000000_CHARA_BASE.cysp', '106301_BATTLE.cysp', '106331_BATTLE.json'); | |
| $baseSkel = readCyspSkeleton('common/000000_CHARA_BASE.cysp'); | |
| file_put_contents('common/000000_CHARA_BASE.json', json_encode(processToJson($baseSkel), JSON_UNESCAPED_SLASHES)); | |
| $baseSkel['animation'] = readCyspAnimation('106301_BATTLE.cysp', $baseSkel); | |
| $json = processToJson($baseSkel)['animations']; | |
| file_put_contents('106301_BATTLE.json', json_encode($json, JSON_UNESCAPED_SLASHES)); | |
| chdir('common'); | |
| foreach (glob('*.cysp.txt') as $file) { | |
| echo "$file\n"; | |
| /*$skel = ['skeleton'=>[],'animation'=>[]]; | |
| $animFs = new FileStream($file); | |
| $animFs->littleEndian = true; | |
| $animFs->position = 12; | |
| $animationCount = $animFs->long; | |
| $animFs->littleEndian = false; | |
| $animFs->position = ($animationCount + 1) * 32; | |
| for ($i=0; $i<$animationCount; $i++) { | |
| $skel['animation'][] = readAnimation($animFs); | |
| }*/ | |
| $baseSkel['animation'] = readCyspAnimation($file, $baseSkel); | |
| $json = processToJson($baseSkel)['animations']; | |
| file_put_contents( | |
| pathinfo(pathinfo($file, PATHINFO_FILENAME), PATHINFO_FILENAME) . '.json', | |
| json_encode($json, JSON_UNESCAPED_SLASHES) | |
| ); | |
| //exportCyspSkel('../000000_CHARA_BASE.cysp', $file, pathinfo(pathinfo($file, PATHINFO_FILENAME), PATHINFO_FILENAME) . '.json'); | |
| } | |
| /*$fs = new FileStream('106301_BATTLE.cysp'); | |
| $fs->position = 32*5; | |
| print_r(readAnimation($fs));*/ | |
| } |
Are we able to use .cysp files in the modified WebGL runtime
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@Krulu
Converter is discontinued.
Check the steps in comment at the beginning for local viewing.