한글 에러 : ArgumentError: Error #2180: AVM1 내용(AS1 또는 AS2)이 AVM2(AS3) 내용으로 로드된 경우 AVM1 내용을 displayLis의 다른 부분으로 이동할 수 없습니다."

 

영문 에러 : ArgumentError: Error #2180: It is illegal to move AVM1 content (AS1 or AS2) to a different part of the displayList when it has been loaded into AVM2 (AS3) content.


위와 같은 에러를 본 적이 있는가?

 

이것은 Flash Player 9 버전으로 만들어진 애플리케이션에서는 발생하지 않는다. Flash Player 10 버전으로 만들어진 애플리케이션에서 위와 같은 문제가 발생한다.

 

위 에러가 발생하는 조건은 다음과 같다.

 

  1. AVM2(ActionScript Virtual Machine 2)기반으로 만들어진 Flash 애플리케이션
    AVM2 기반이라는 것은 ActionScript 3.0 기반으로 만들어진 애플리케이션을 의미한다.
  2. Flash Player 10 기반으로 컴파일한다.
    컴파일 옵션에서 Flash Player 버전을 9.0.124 등이 아닌 10.0.0을 기반으로 한다.
  3. 위 두 조건하에서 AVM1기반으로 만들어진 SWF파일을 flash.display.Loader를 이용해 로드한다.
    AVM1 기반이라는 것은 ActionScript 1.0 또는 ActionScript 2.0 기반으로 만든 SWF파일을 의미한다.
  4. Loader.contentLoaderInfo.content를 addChild() 한다.
    Loader는 외부의 이미지, 플래시 파일등을 로드해서 디스플레이 객체로 등록하는 일종의 wrapper역할을 담당한다. 이들을 로드완료하면 Loader.contentLoaderInfo.content에 해당 객체의 참조를 얻을 수 있다. 일반적으로 화면에 출력하기 위해 addChild( Loader ) 식으로 하면 되는데 이렇게 안하고 addChild( Loader.contentLoaderInfo.content ) 를 하는 경우다. 이것이 가능한 이유는 Loader.contentLoaderInfo.content 도 DisplayObject이기 때문이다.

 

위 조건에 맞춰서 ActionScript 3.0 기반으로 코딩을 아래와 같이 해보겠다.

 

package
{
	import flash.display.DisplayObject;
	import flash.display.Loader;
	import flash.display.LoaderInfo;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.net.URLRequest;
	
	public class AVM1LoadTest extends Sprite
	{
		private var loader:Loader;
		
		public function AVM1LoadTest()
		{
			loader = new Loader();
			loader.contentLoaderInfo.addEventListener(Event.COMPLETE,onLoadComplete);
			loader.load( new URLRequest("avm1asset.swf") );	
		}
		
		private function onLoadComplete( event:Event ):void
		{
			var loaderInfo:LoaderInfo = event.target as LoaderInfo;
			trace( loader.numChildren );
			trace( loaderInfo.content.parent );
			var avm1asset:DisplayObject = loaderInfo.content;
			addChild( avm1asset );
			trace( loader.numChildren );
			trace( loaderInfo.content.parent );
		}
	}
}

 

 

위 프로그램을 Flash Player 9 버전으로 컴파일하고 실행해보자. (실행되는 Flash Player 버전은 10이어야 한다. 당연히 실험 애플리케이션이 Flash Player 9버전 뿐 아니라 10버전도 만들 것이기 때문이다.)

 

아래 화면과 같이 AVM1 기반 SWF를 예쁘게 출력해준다.

 

 

onLoadComplete() 안을 살펴보면 addChild() 앞뒤로 똑같은 trace()문이 있다. 이것은 addChild() 전후로 loader.numChildren과 loaderInfo.content.parent(이것은 loader.content.parent와 같다)의 변화를 살펴보기 위함이다. loader.numChildren은 Loader 객체에 로드된 AVM1객체가 자식으로 등록되어 있는 경우에는 1이 출력되고 그 반대는 0이 된다. loaderInfo.content.parent는 로드된 AVM1객체의 부모가 무엇인지 가리킨다.

 

결과는 다음과 같다.

 

1
[object Loader]
0
[object AVM1LoadTest]

 

무엇을 의미하는가? addChild()를 하기 전에는 AVM1객체는 Loader의 자식이지만 addChild() 후로는 AVM1LoadTest객체의 자식이 된다. AVM1LoadTest는 위 프로그램의 메인 클래스이다. 이것은 당연한 결과로 자식은 두개 이상의 부모를 가질 수 없는 것을 의미한다. Flash Player 9버전으로 만든 경우에는 이러한 코딩이 가능했다.

 

조건을 바꿔보자. 이제 위 프로그램을 Flash Player 10 버전으로 컴파일하고 실행해보자. 언급했던 에러가 발생한다.

 

"ArgumentError: Error #2180: AVM1 내용(AS1 또는 AS2)이 AVM2(AS3) 내용으로 로드된 경우 AVM1 내용을 displayLis의 다른 부분으로 이동할 수 없습니다."

 

무엇을 의미하는가? Flash Player 10 기반으로 만들어진 애플리케이션은 AVM1 객체를 AVM2 기반의 디스플레이 객체로의 이동을 불허한다. 즉 Loader에서 떠나지 못함을 의미한다.

 

Flash Player 10기반에서 AVM2 SWF를 로드한 경우는 addChild( loader.contentLoaderInfo.content ) 가 가능할까? 이것은 된다!

 

Flash Player 10에서 AVM1기반의 SWF는 부모로 Loader이어야만 하는 이유는 무엇일까? 사실 여기에 대한 답변을 나는 아직 찾지 못했다. 혹시 아시는 분은 댓글 및 트랙백을 부탁한다.

 

일본에서는 이미 이 문제로 고민한 사람이 꽤 있었다. 재미있게도 AVM1을 AVM2로 강제로 바이트 단위로 버전을 수정해 버려 로드해버리는 커스텀 Loader까지 만들어 배포하는 사람이 있었다. 좋은 방법인지는 모르겠지만 아무튼 이런 연구가 활발히 진행되는 일본이 부럽당.. ㅡㅡ

 

Illustrator의 벡터형식 데이터를 읽어들이는 방법(Loader 클래스편)

Illustrator의 벡터형식 데이터를 읽어들이는 방법(Embed 태그 편)

BeInteractive - AVM2에서 AVM1의 SWF를 억지로 로드하는 ForcibleLoader 

BeInteractive - AVM2에서 AVM2의 SWF를 억지로 로드하는 방식 해설

ForcibleLoader 다운로드

ForcibleLoader 브라우징하기

 

흑... 이것도 spark 프로젝트....


아래 내용도 보면 좋을거다.

로드한 SWF 내부에 작성된 ActionScript 3.0 클래스 이름 찾기

 

글쓴이 : 지돌스타(http://blog.jidolstar.com/582)

 

ActionScript 2.0 기반으로 만들어진 swf는 AVM1이며 ActionScript 3.0기반에서 작성된 것 swf는 AVM2이다. AVM2와 AVM1 간에 스크립트 통신이 직접적으로 진행되지 않고 LocalConnection만으로만 가능하다.

 

여기서 소개하는 AVM2Loader는 AVM1, AVM2 기반의 SWF를 모두 AVM2의 형태로 로드시켜 줌으로써 해당 SWF간 스크립트 통신이 가능하게 하는데 목적이 있다. 구현방법은 flash.display.Loader 클래스를 확장해 로드된 컨텐츠가 AVM1인 경우는 AVM2로 변경해준다. 정확한 의미는 본인도 잘 모른다. 시간이 허락한다면 분석해보려고 한다.

 

소개하는 자료는 TroyWorks Blog에 나온 자료임을 밝혀둔다. 직접 테스트는 안해봤다. ^^ 아래코드는 AVM2Loader이다.

package
{
	import flash.display.Loader;
	import flash.events.*;
	import flash.net.*;
	import flash.system.LoaderContext;
	import flash.utils.ByteArray;
	import flash.utils.Endian;

	/**
	 * Loads both of AVM1 and AVM2 swf as AVM2.
	 */
	public class AVM2Loader extends Loader
	{
		private var _urlLoader:URLLoader;
		private var _context:LoaderContext;

		/**
		 * loads both of AVM1 and AVM2 movie as AVM2 movie.
		 */
		override public function load(request:URLRequest,context:LoaderContext=null):void
		{
			_context=context;

			_urlLoader=new URLLoader ;
			_urlLoader.dataFormat=URLLoaderDataFormat.BINARY;
			_urlLoader.addEventListener(Event.COMPLETE,_binaryLoaded,false,0,true);
			_urlLoader.addEventListener(IOErrorEvent.IO_ERROR,_transferEvent,false,0,true);
			_urlLoader.addEventListener(ProgressEvent.PROGRESS,_transferEvent,false,0,true);
			_urlLoader.addEventListener(Event.OPEN,_transferEvent,false,0,true);
			_urlLoader.addEventListener(HTTPStatusEvent.HTTP_STATUS,_transferEvent,false,0,true);
			_urlLoader.addEventListener(SecurityErrorEvent.SECURITY_ERROR,_transferEvent,false,0,true);
			_urlLoader.load(request);
		}

		private function _binaryLoaded(e:Event):void
		{
			loadBytes(ByteArray(_urlLoader.data),_context);
			_urlLoader=null;
		}

		private function _transferEvent(e:Event):void
		{
			dispatchEvent(e);
		}

		/**
		 * loads both of AVM1 and AVM2 movie as AVM2 movie.
		 */
		override public function loadBytes(bytes:ByteArray,context:LoaderContext=null):void
		{
			//uncompress if compressed
			bytes.endian=Endian.LITTLE_ENDIAN;
			trace("loadBytes" + bytes[0] );
			if (bytes[0] == 0x43) {
				//trace(" hackA");
				//many thanks for be-interactive.org
				var compressedBytes:ByteArray=new ByteArray() ;
				compressedBytes.writeBytes(bytes,8);
				compressedBytes.uncompress();

				bytes.length=8;
				bytes.position=8;
				bytes.writeBytes(compressedBytes);
				compressedBytes.length=0;

				//flag uncompressed
				bytes[0]=0x46;
			}
			hackBytes(bytes);
			super.loadBytes(bytes,context);
		}

		/**
		 * if bytes are AVM1 movie, hack it!
		 */
		private function hackBytes(bytes:ByteArray):void
		{
			//trace(" hackB");
			if (bytes[4] < 0x09)
			{
				trace("hack 9");
				bytes[4]=0x09;
			}

			//trace(" hackC");
			//dirty dirty
			var imax:int=Math.min(bytes.length,100);
			for (var i:int=23; i < imax; i++)
			{
				if (bytes[i - 2] == 0x44 && bytes[i - 1] == 0x11)
				{
					bytes[i]=bytes[i] | 0x08;
					return;
				}
			}
		}
	}
}
아래는 AVM2Loader 테스트 소스이다.
import flash.display.DisplayObject;
import flash.display.Loader;
import flash.display.LoaderInfo;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.net.URLLoader;
import flash.net.URLLoaderDataFormat;
import flash.net.URLRequest;
import flash.utils.ByteArray;
import flash.display.SimpleButton;
import AVM2Loader;

var avm1:AVM1Movie;

function testAudio(e:Event = null):void
{
	trace("testAudio");

	//var request:URLRequest = new URLRequest("video.swf");
	var request:URLRequest = new URLRequest("Flash8Movie.swf");
	var ldr:Loader;
	if (true)
	{
		trace("start AVM2Loader");
		ldr = new AVM2Loader();
	}
	else
	{
		ldr = new Loader();
	}

	ldr.contentLoaderInfo.addEventListener(Event.COMPLETE, _onLoaderComplete);
	ldr.load(request);
}

function _onLoaderComplete(event:Event):void
{
	trace(" _onLoaderComplete");
	var info:LoaderInfo = event.target as LoaderInfo;
	trace(" _onLoaderComplete " + info.content);

	if (info.content is AVM1Movie)
	{
		avm1 = AVM1Movie(info.content);
	}
	else
	{
		addChild(info.content);
		MovieClip(info.content).stop();
		var dO:DisplayObject = DisplayObject(info.content);
	}
}

stage.addEventListener(MouseEvent.CLICK, testAudio);
testAudio();
 
관련글

 

+ Recent posts