using static Const;
using static com.Company.Library;
using System.Collections.Generic;
using System.Collections;
using System.IO;
using System.Linq;
using System.Reflection;
using System;
using UnityEngine.SceneManagement;
using UnityEngine;
public static partial class Program {
	private static class Graph {
		public static GameObject			goRoot;
		public static int				layer;
		public static int				sortingOrder;
		public static MaterialPool			materialPool		= new MaterialPool();
		public static ObjectPool<MovieObject>		movieObjectPool		= new ObjectPool<MovieObject>();
		public static ObjectPool<SpriteObject>		spriteObjectPool	= new ObjectPool<SpriteObject>();
		public static ObjectPool<LineObject>		lineObjectPool		= new ObjectPool<LineObject>();
		public static ObjectPool<BoxObject>		boxObjectPool		= new ObjectPool<BoxObject>();
		public static ObjectPool<PolygonObject>		polygonObjectPool	= new ObjectPool<PolygonObject>();
		public static Dictionary<int,ST_TexDef>		texDefCache		= new Dictionary<int,ST_TexDef>();
		public static Dictionary<int,ST_SprDef>		sprDefCache		= new Dictionary<int,ST_SprDef>();
		public static Dictionary<int,Texture2D>		textureCache		= new Dictionary<int,Texture2D>();
		public static Dictionary<int,Sprite>		spriteCache		= new Dictionary<int,Sprite>();
		public static ST_MovieHelper			pMovieHelper;
	}
	private class MaterialPool {
		private List<Material>	TBL_Material	= new List<Material>();
		private int		Count;
		public Material GetMaterial(Texture texture, Color color) {
			Material material;
			for(int i = 0; i < Count; i++) {
				material = TBL_Material[i];
				if((material.mainTexture == texture) && (material.color == color)) { return material; }
			}
			if(Count == TBL_Material.Count) {
				TBL_Material.Add(new Material(Shader.Find("Sprites/Default")));
			}
			material = TBL_Material[Count++];
			material.mainTexture = texture;
			material.color = color;
			return material;
		}
		public void Reset() {
			Count = 0;
		}
	}
	private class ObjectPool<T> where T : PoolObject, new() {
		private List<T>		TBL_PoolObject	= new List<T>();
		private int		Count;
		public T GetObject() {
			if(Count == TBL_PoolObject.Count) {
				TBL_PoolObject.Add(new T());
			}
			T po = TBL_PoolObject[Count++];
			po.SetActive(true);
			po.SetLayer(Graph.layer);
			po.SetSortingOrder(Graph.sortingOrder++);
			return po;
		}
		public void Reset() {
			while(Count != 0) {
				T po = TBL_PoolObject[--Count];
				po.SetActive(false);
			}
		}
	}
	private abstract class PoolObject {
		public GameObject		goOuter;
		public GameObject		goInner;
		public abstract Renderer	renderer { get; }
		protected PoolObject() {
			goOuter = new GameObject();
			goOuter.transform.SetParent(Graph.goRoot.transform, false);
		}
		public void SetActive(bool value) {
			goOuter.SetActive(value);
		}
		public void SetLayer(int layer) {
			goInner.layer = layer;
		}
		public void SetSortingOrder(int sortingOrder) {
			renderer.sortingOrder = sortingOrder;
		}
	}
	private class MovieObject : PoolObject {
		public MeshRenderer	meshRenderer;
		public MeshFilter	meshFilter;
		public MovieObject() {
			goInner = GameObject.CreatePrimitive(PrimitiveType.Quad);
			goInner.transform.SetParent(goOuter.transform, false);
			meshRenderer = goInner.GetComponent<MeshRenderer>();
			if(meshRenderer == null) { throw new ApplicationException(); }
			meshFilter = goInner.GetComponent<MeshFilter>();
			if(meshFilter == null) { throw new ApplicationException(); }
		}
		public override Renderer renderer {
			get {
				return meshRenderer;
			}
		}
	}
	private class SpriteObject : PoolObject {
		public SpriteRenderer	spriteRenderer;
		public SpriteObject() {
			goInner = new GameObject(null, typeof(SpriteRenderer));
			goInner.transform.SetParent(goOuter.transform, false);
			spriteRenderer = goInner.GetComponent<SpriteRenderer>();
			if(spriteRenderer == null) { throw new ApplicationException(); }
		}
		public override Renderer renderer {
			get {
				return spriteRenderer;
			}
		}
	}
	private class LineObject : PoolObject {
		public LineRenderer	lineRenderer;
		public LineObject() {
			goInner = new GameObject();
			goInner.transform.SetParent(goOuter.transform, false);
			goInner.transform.localPosition = new Vector2(
				(float)(-(DISP_X / 2) / PPU),
				(float)( (DISP_Y / 2) / PPU));
			goInner.transform.localScale = new Vector2(
				(float)( 1.0 / PPU),
				(float)(-1.0 / PPU));
			lineRenderer = goInner.AddComponent<LineRenderer>();
			lineRenderer.useWorldSpace = false;
		}
		public override Renderer renderer {
			get {
				return lineRenderer;
			}
		}
	}
	private class BoxObject : PoolObject {
		public MeshRenderer	meshRenderer;
#if     !USE_COLOR_MATERIAL
		public MeshFilter	meshFilter;
#endif
		public BoxObject() {
			goInner = GameObject.CreatePrimitive(PrimitiveType.Quad);
			goInner.transform.SetParent(goOuter.transform, false);
			goInner.transform.localPosition = new Vector2(
				(float)( 0.5 / PPU),
				(float)(-0.5 / PPU));
			goInner.transform.localScale = new Vector2(
				(float)( 1.0 / PPU),
				(float)( 1.0 / PPU));
			meshRenderer = goInner.GetComponent<MeshRenderer>();
			if(meshRenderer == null) { throw new ApplicationException(); }
#if     !USE_COLOR_MATERIAL
			meshFilter = goInner.GetComponent<MeshFilter>();
			if(meshFilter == null) { throw new ApplicationException(); }
#endif
		}
		public override Renderer renderer {
			get {
				return meshRenderer;
			}
		}
	}
	private class PolygonObject : PoolObject {
		public MeshRenderer	meshRenderer;
		public MeshFilter	meshFilter;
		public PolygonObject() {
			goInner = new GameObject();
			goInner.transform.SetParent(goOuter.transform, false);
			goInner.transform.localPosition = new Vector2(
				(float)(-(DISP_X / 2) / PPU),
				(float)( (DISP_Y / 2) / PPU));
			goInner.transform.localScale = new Vector2(
				(float)( 1.0 / PPU),
				(float)(-1.0 / PPU));
			meshRenderer = goInner.AddComponent<MeshRenderer>();
			meshFilter = goInner.AddComponent<MeshFilter>();
			meshFilter.mesh = new Mesh();
		}
		public override Renderer renderer {
			get {
				return meshRenderer;
			}
		}
	}
	public class ST_TexDef {
		public string		sPath;
		public bool		bLoop;
		public bool		bAdd;
		public int		nScale;
		public bool	isSprite {
			get {
				return string.IsNullOrEmpty(Path.GetExtension(sPath));
			}
		}
		public bool	isMovie {
			get {
				return !isSprite;
			}
		}
	}
	public class ST_SprDef {
		public int		iTexNo;
		public int		x, y;
		public int		w, h;
		public int		cx, cy;
		public bool	isSprite {
			get {
				return GetTexDef(iTexNo).isSprite;
			}
		}
		public bool	isMovie {
			get {
				return GetTexDef(iTexNo).isMovie;
			}
		}
	}
	public class MoviePlayer : IMoviePlayer {
		public CriManaMovieMaterial	criManaMovieMaterial;
		public MoviePlayer() {
			criManaMovieMaterial = Graph.goRoot.AddComponent<CriManaMovieMaterial>();
		}
		void IDisposable.Dispose() {
			UnityEngine.Object.Destroy(criManaMovieMaterial);
		}
		MoviePlayerStatus IMoviePlayer.GetStatus() {
			return (MoviePlayerStatus)criManaMovieMaterial.player.status;
		}
		void IMoviePlayer.Prepare(int iTexNo) {
			if((iTexNo < 1) || (iTexNo > TexNo_Max)) { throw new ApplicationException(); }
			ST_TexDef stTexDef = GetTexDef(iTexNo);
			if(!criManaMovieMaterial.player.SetFile(binder, stTexDef.sPath)) { throw new ApplicationException(); }
			criManaMovieMaterial.player.Loop(stTexDef.bLoop);
			criManaMovieMaterial.player.additiveMode = stTexDef.bAdd;
			criManaMovieMaterial.player.Prepare();
		}
		void IMoviePlayer.Start() {
			criManaMovieMaterial.player.Start();
			for(;;) {
				if(criManaMovieMaterial.player.status == CriMana.Player.Status.Error) { throw new ApplicationException(); }
				if(criManaMovieMaterial.isMaterialAvailable) { break; }
				CriManaMovieMaterial.RenderMode save_renderMode = criManaMovieMaterial.renderMode;
				criManaMovieMaterial.renderMode = CriManaMovieMaterial.RenderMode.Never;
				typeof(CriManaMovieMaterial).InvokeMember("Update", BindingFlags.InvokeMethod|BindingFlags.NonPublic, null, criManaMovieMaterial, null);
				criManaMovieMaterial.renderMode = save_renderMode;
			}
		}
		void IMoviePlayer.Stop() {
			criManaMovieMaterial.Stop();
			for(;;) {
				CriMana.Player.Status status = criManaMovieMaterial.player.status;
				if(status == CriMana.Player.Status.Error) { throw new ApplicationException(); }
				if(status == CriMana.Player.Status.Stop) { break; }
				CriManaMovieMaterial.RenderMode save_renderMode = criManaMovieMaterial.renderMode;
				criManaMovieMaterial.renderMode = CriManaMovieMaterial.RenderMode.Never;
				typeof(CriManaMovieMaterial).InvokeMember("Update", BindingFlags.InvokeMethod|BindingFlags.NonPublic, null, criManaMovieMaterial, null);
				criManaMovieMaterial.renderMode = save_renderMode;
			}
		}
	}
	public static void Graph_Init() {
		Graph.goRoot = new GameObject();
		Graph.goRoot.hideFlags = HideFlags.HideInHierarchy;
		UnityEngine.Object.DontDestroyOnLoad(Graph.goRoot);
		int nCh = criWareInitializer.manaConfig.numberOfDecoders / 2;
		Graph.pMovieHelper = MovieHelper_New(nCh, delegate(object arg) { return new MoviePlayer(); }, null);
	}
	public static void Graph_Exec() {
		Graph.layer = 0;
		Graph.sortingOrder = short.MinValue;
		Graph.materialPool.Reset();
		Graph.movieObjectPool.Reset();
		Graph.spriteObjectPool.Reset();
		Graph.lineObjectPool.Reset();
		Graph.boxObjectPool.Reset();
		Graph.polygonObjectPool.Reset();
		MovieHelper_Exec(Graph.pMovieHelper);
	}
	public static void Graph_Exit() {
		MovieHelper_Exit(Graph.pMovieHelper);
		UnityEngine.Object.Destroy(Graph.goRoot);
	}
	public static void StableAspect() {
		Camera camera = Camera.main;
		if(camera == null) { return; }
		camera.orthographicSize = (float)((DISP_Y / PPU) / 2.0);
		double scrAspect = (double)Screen.height / (double)Screen.width;
		double bgAspect = (double)DISP_Y / (double)DISP_X;
		if(bgAspect >= scrAspect) {
			double camWidth = scrAspect / bgAspect;
			camera.rect = new Rect((float)((1.0 - camWidth) / 2.0), 0, (float)camWidth, 1);
		} else {
			double camHeight = bgAspect / scrAspect;
			camera.rect = new Rect(0, (float)((1.0 - camHeight) / 2.0), 1, (float)camHeight);
		}
	}
	private static CriManaMovieMaterial GetMovieMaterial(int iTexNo) {
		MoviePlayer moviePlayer = (MoviePlayer)MovieHelper_Draw(Graph.pMovieHelper, iTexNo);
		return moviePlayer.criManaMovieMaterial;
	}
	private static Texture2D GetTexture(int iTexNo) {
		Texture2D texture;
		if((iTexNo < 1) || (iTexNo > TexNo_Max)) { throw new ApplicationException(); }
		if(!Graph.textureCache.TryGetValue(iTexNo, out texture)) {
			ST_TexDef stTexDef = GetTexDef(iTexNo);
			texture = Resources.Load<Texture2D>(stTexDef.sPath);
			if(texture == null) { throw new ApplicationException(); }
			Graph.textureCache.Add(iTexNo, texture);
		}
		return texture;
	}
	private static Sprite GetSprite(int iSprNo) {
		Sprite sprite;
		if((iSprNo < 1) || (iSprNo > SprNo_Max)) { throw new ApplicationException(); }
		if(!Graph.spriteCache.TryGetValue(iSprNo, out sprite)) {
			ST_SprDef stSprDef = GetSprDef(iSprNo);
			Texture2D texture = GetTexture(stSprDef.iTexNo);
			Rect rect = new Rect(stSprDef.x,
			   texture.height - (stSprDef.y + stSprDef.h),
			                     stSprDef.w,
			                     stSprDef.h);
			sprite = Sprite.Create(texture, rect, Vector2.zero);
			Graph.spriteCache.Add(iSprNo, sprite);
		}
		return sprite;
	}
	public static ST_SprDef GetSprDef(int iSprNo) {
		ST_SprDef stSprDef;
		if((iSprNo < 1) || (iSprNo > SprNo_Max)) { throw new ApplicationException(); }
		if(!Graph.sprDefCache.TryGetValue(iSprNo, out stSprDef)) {
			VoidPtr pSprDef = REG_open_key_l(TBL_RegTbl, RegKey_SprDef, iSprNo);
			if(!pSprDef) { throw new ApplicationException(); }
			stSprDef = new ST_SprDef() {
				iTexNo = REG_get_value(pSprDef, RegKey_TexNo),
				x      = REG_get_value(pSprDef, RegKey_x),
				y      = REG_get_value(pSprDef, RegKey_y),
				w      = REG_get_value(pSprDef, RegKey_w),
				h      = REG_get_value(pSprDef, RegKey_h),
				cx     = REG_get_value(pSprDef, RegKey_cx),
				cy     = REG_get_value(pSprDef, RegKey_cy),
			};
			if((stSprDef.iTexNo == -1) ||
			   (stSprDef.x      == -1) ||
			   (stSprDef.y      == -1) ||
			   (stSprDef.w      == -1) ||
			   (stSprDef.h      == -1) ||
			   (stSprDef.cx     == -1) ||
			   (stSprDef.cy     == -1)) { throw new ApplicationException(); }
			Graph.sprDefCache.Add(iSprNo, stSprDef);
		}
		return stSprDef;
	}
	private static ST_TexDef GetTexDef(int iTexNo) {
		ST_TexDef stTexDef;
		if((iTexNo < 1) || (iTexNo > TexNo_Max)) { throw new ApplicationException(); }
		if(!Graph.texDefCache.TryGetValue(iTexNo, out stTexDef)) {
			VoidPtr pTexDef = REG_open_key_l(TBL_RegTbl, RegKey_TexDef, iTexNo);
			if(!pTexDef) { throw new ApplicationException(); }
			string sPath = REG_get_string(pTexDef, RegKey_Path);
			if(sPath == null) { throw new ApplicationException(); }
			bool bLoop = (REG_get_value(pTexDef, RegKey_loop) == 1);
			bool bAdd = (REG_get_value(pTexDef, RegKey_add) == 1);
			int nScale = Math.Abs(REG_get_value(pTexDef, RegKey_scale));
			stTexDef = new ST_TexDef() {
				sPath = sPath,
				bLoop = bLoop,
				bAdd = bAdd,
				nScale = nScale,
			};
			Graph.texDefCache.Add(iTexNo, stTexDef);
		}
		return stTexDef;
	}
	public static bool prep_spr(int iSprNo) {
		ST_SprDef stSprDef = GetSprDef(iSprNo);
		if(stSprDef.isMovie) {
			return MovieHelper_Prepare(Graph.pMovieHelper, stSprDef.iTexNo);
		} else {
			return true;
		}
	}
	private static void LoadScene(string sceneName) {
		SceneManager.LoadScene(sceneName);
	}
	public static void CbkQue_LoadScene(ST_CbkQue pCbkQue, int pri, string sceneName) {
		CbkQue_Add(pCbkQue, pri, delegate(ST_CbkQue _pCbkQue, int _pri, object param) {
				LoadScene(sceneName);
			}, null);
	}
	private static void SetLayer(int layer) {
		Graph.layer = layer;
	}
	public static void CbkQue_SetLayer(ST_CbkQue pCbkQue, int pri, int layer) {
		CbkQue_Add(pCbkQue, pri, delegate(ST_CbkQue _pCbkQue, int _pri, object param) {
				SetLayer(layer);
			}, null);
	}
	private static void SetSortingOrder(int sortingOrder) {
		Graph.sortingOrder = sortingOrder;
	}
	public static void CbkQue_SetSortingOrder(ST_CbkQue pCbkQue, int pri, int sortingOrder) {
		CbkQue_Add(pCbkQue, pri, delegate(ST_CbkQue _pCbkQue, int _pri, object param) {
				SetSortingOrder(sortingOrder);
			}, null);
	}
	private static void draw_spr(double x, double y, int iSprNo, double scale_x, double scale_y, double rot, int rgba) {
		ST_SprDef stSprDef = GetSprDef(iSprNo);
		PoolObject po;
		if(stSprDef.isMovie) {
			CriManaMovieMaterial movieMaterial = GetMovieMaterial(stSprDef.iTexNo);
			if(movieMaterial == null) { throw new ApplicationException(); }
			MovieObject mo = Graph.movieObjectPool.GetObject();
			mo.meshRenderer.material = movieMaterial.material;
			Mesh mesh = mo.meshFilter.mesh;
			Color32 c = RGBA_TOCOLOR(rgba);
			mesh.colors32 = new Color32[] { c,c,c,c };
			mo.meshFilter.mesh = mesh;
			mo.goInner.transform.localPosition = new Vector2(
				(float)(((stSprDef.w / 2) - (stSprDef.cx)) / PPU),
				(float)(((stSprDef.cy) - (stSprDef.h / 2)) / PPU));
			mo.goInner.transform.localScale = new Vector2(
				(float)(stSprDef.w / PPU),
				(float)(stSprDef.h / PPU));
			po = mo;
		} else {
			Sprite sprite = GetSprite(iSprNo);
			SpriteObject so = Graph.spriteObjectPool.GetObject();
			so.spriteRenderer.sprite = sprite;
			so.spriteRenderer.color = RGBA_TOCOLOR(rgba);
			so.goInner.transform.localPosition = new Vector2(
				(sprite.pivot.x - stSprDef.cx) / sprite.pixelsPerUnit,
				(stSprDef.cy - (sprite.rect.height - sprite.pivot.y)) / sprite.pixelsPerUnit);
			po = so;
		}
		ST_TexDef stTexDef = GetTexDef(stSprDef.iTexNo);
		if(stTexDef.nScale != 1) {
			scale_x *= stTexDef.nScale;
			scale_y *= stTexDef.nScale;
		}
		po.goOuter.transform.localPosition = new Vector2(
			(float)((x - (DISP_X / 2)) / PPU),
			(float)(((DISP_Y / 2) - y) / PPU));
		po.goOuter.transform.localScale = new Vector2(
			(float)scale_x,
			(float)scale_y);
		po.goOuter.transform.localEulerAngles = new Vector3(
			0,
			0,
			(float)rot * -Mathf.Rad2Deg);
	}
	public static void CbkQue_draw_spr(ST_CbkQue pCbkQue, int pri, double x, double y, int iSprNo, double scale_x = 1, double scale_y = 1, double rot = 0, int rgba = -1) {
		CbkQue_Add(pCbkQue, pri, delegate(ST_CbkQue _pCbkQue, int _pri, object param) {
				draw_spr(x, y, iSprNo, scale_x, scale_y, rot, rgba);
			}, null);
	}
	private static void draw_sprf_fn(object user_data, int x, int y, double scale_x, double scale_y, double rot, int rgba, int iSprNo) {
		draw_spr(x, y, iSprNo, scale_x, scale_y, rot, rgba);
	}
	private static void draw_sprf(VoidPtr pSprFnt, double x, double y, double scale_x, double scale_y, double rot, double ax, double ay, int rgba, string s) {
		SprFnt_Print(pSprFnt, draw_sprf_fn, null, (int)x, (int)y, scale_x, scale_y, rot, ax, ay, rgba, s);
	}
	public static void CbkQue_draw_sprf(ST_CbkQue pCbkQue, int pri, VoidPtr pSprFnt, double x, double y, double scale_x, double scale_y, double rot, double ax, double ay, int rgba, string s) {
		CbkQue_Add(pCbkQue, pri, delegate(ST_CbkQue _pCbkQue, int _pri, object param) {
				draw_sprf(pSprFnt, x, y, scale_x, scale_y, rot, ax, ay, rgba, s);
			}, null);
	}
	private static void draw_sprf(VoidPtr pSprFnt, double x, double y, double scale_x, double scale_y, double rot, double ax, double ay, int rgba, string fmt, params object[] ap) {
		SprFnt_PrintF(pSprFnt, draw_sprf_fn, null, (int)x, (int)y, scale_x, scale_y, rot, ax, ay, rgba, fmt, ap);
	}
	public static void CbkQue_draw_sprf(ST_CbkQue pCbkQue, int pri, VoidPtr pSprFnt, double x, double y, double scale_x, double scale_y, double rot, double ax, double ay, int rgba, string fmt, params object[] ap) {
		CbkQue_Add(pCbkQue, pri, delegate(ST_CbkQue _pCbkQue, int _pri, object param) {
				draw_sprf(pSprFnt, x, y, scale_x, scale_y, rot, ax, ay, rgba, fmt, ap);
			}, null);
	}
	private static void draw_sprf_xf(VoidPtr pSprFnt, double x, double y, double scale_x, double scale_y, double rot, double ax, double ay, int rgba, string s1, string s2, double transition) {
		SprFnt_PrintXF(pSprFnt, draw_sprf_fn, null, (int)x, (int)y, scale_x, scale_y, rot, ax, ay, rgba, s1, s2, transition);
	}
	public static void CbkQue_draw_sprf_xf(ST_CbkQue pCbkQue, int pri, VoidPtr pSprFnt, double x, double y, double scale_x, double scale_y, double rot, double ax, double ay, int rgba, string s1, string s2, double transition) {
		CbkQue_Add(pCbkQue, pri, delegate(ST_CbkQue _pCbkQue, int _pri, object param) {
				draw_sprf_xf(pSprFnt, x, y, scale_x, scale_y, rot, ax, ay, rgba, s1, s2, transition);
			}, null);
	}
	private static Dictionary<int,Texture2D> draw_polyline_singleTextureCache = new Dictionary<int,Texture2D>();
	private static Texture2D draw_polyline_GetSingleTexture(int iSprNo) {
		Texture2D singleTexture;
		if((iSprNo < 1) || (iSprNo > SprNo_Max)) { throw new ApplicationException(); }
		if(!draw_polyline_singleTextureCache.TryGetValue(iSprNo, out singleTexture)) {
			string path = EnmDef_GetName(pEnmKey, RegKey_SprNo, iSprNo);
			if(path.StartsWith("G_")) { path = path.Substring(2); }
			path = path.ToLower();
			singleTexture = Resources.Load<Texture2D>(path);
			if(singleTexture == null) { throw new ApplicationException(); }
			draw_polyline_singleTextureCache.Add(iSprNo, singleTexture);
		}
		return singleTexture;
	}
	private static void draw_polyline(double[,] xy, double w, int iSprNo, int rgba) {
		LineObject lo = Graph.lineObjectPool.GetObject();
		int n = xy.GetLength(0);
		lo.lineRenderer.positionCount = n;
		lo.lineRenderer.startWidth = lo.lineRenderer.endWidth = (float)(w / PPU);
		lo.lineRenderer.startColor = lo.lineRenderer.endColor = RGBA_TOCOLOR(rgba);
		Vector3[] positions = new Vector3[n];
		for(int i = 0; i < n; i++) {
			positions[i] = new Vector2(
				(float)xy[i,0],
				(float)xy[i,1]);
		}
		lo.lineRenderer.SetPositions(positions);
		lo.lineRenderer.loop = false;
		if(iSprNo != -1) {
			ST_SprDef stSprDef = GetSprDef(iSprNo);
			if(stSprDef.isMovie) {
				throw new ApplicationException();
			} else {
				Texture2D singleTexture = draw_polyline_GetSingleTexture(iSprNo);
				lo.lineRenderer.material = Graph.materialPool.GetMaterial(singleTexture, Color.white);
			}
		} else {
			lo.lineRenderer.material = Graph.materialPool.GetMaterial(null, Color.white);
		}
	}
	public static void CbkQue_draw_polyline(ST_CbkQue pCbkQue, int pri, double[,] xy, double w, int iSprNo, int rgba = -1) {
		CbkQue_Add(pCbkQue, pri, delegate(ST_CbkQue _pCbkQue, int _pri, object param) {
				draw_polyline(xy, w, iSprNo, rgba);
			}, null);
	}
	private static void draw_box(double x, double y, double w, double h, int rgba) {
		LineObject lo = Graph.lineObjectPool.GetObject();
		lo.lineRenderer.positionCount = 4;
		lo.lineRenderer.startWidth = lo.lineRenderer.endWidth = (float)(1.0 / PPU);
		lo.lineRenderer.startColor = lo.lineRenderer.endColor = RGBA_TOCOLOR(rgba);
		Vector3[] positions = new Vector3[4];
		positions[0] = new Vector2((float)(x    ), (float)(y    ));
		positions[1] = new Vector2((float)(x + w), (float)(y    ));
		positions[2] = new Vector2((float)(x + w), (float)(y + h));
		positions[3] = new Vector2((float)(x    ), (float)(y + h));
		lo.lineRenderer.SetPositions(positions);
		lo.lineRenderer.loop = true;
		lo.lineRenderer.material = Graph.materialPool.GetMaterial(null, Color.white);
	}
	public static void CbkQue_draw_box(ST_CbkQue pCbkQue, int pri, double x, double y, double w, double h, int rgba = -1) {
		CbkQue_Add(pCbkQue, pri, delegate(ST_CbkQue _pCbkQue, int _pri, object param) {
				draw_box(x, y, w, h, rgba);
			}, null);
	}
	private static void draw_box_fill(double x, double y, double w, double h, int rgba) {
		BoxObject bo = Graph.boxObjectPool.GetObject();
		bo.goOuter.transform.localPosition = new Vector2(
			(float)((x - (DISP_X / 2)) / PPU),
			(float)(((DISP_Y / 2) - y) / PPU));
		bo.goOuter.transform.localScale = new Vector2(
			(float)w,
			(float)h);
#if     USE_COLOR_MATERIAL
		bo.meshRenderer.material = Graph.materialPool.GetMaterial(null, RGBA_TOCOLOR(rgba));
#else
		bo.meshRenderer.material = Graph.materialPool.GetMaterial(null, Color.white);
		Mesh mesh = bo.meshFilter.mesh;
		Color32 c = RGBA_TOCOLOR(rgba);
		mesh.colors32 = new Color32[] { c,c,c,c };
		bo.meshFilter.mesh = mesh;
#endif
	}
	public static void CbkQue_draw_box_fill(ST_CbkQue pCbkQue, int pri, double x, double y, double w, double h, int rgba = -1) {
		CbkQue_Add(pCbkQue, pri, delegate(ST_CbkQue _pCbkQue, int _pri, object param) {
				draw_box_fill(x, y, w, h, rgba);
			}, null);
	}
	private static void draw_polygon(double[,] xy, int rgba = -1) {
		LineObject lo = Graph.lineObjectPool.GetObject();
		int n = xy.GetLength(0);
		lo.lineRenderer.positionCount = n;
		lo.lineRenderer.startWidth = lo.lineRenderer.endWidth = (float)(1.0 / PPU);
		lo.lineRenderer.startColor = lo.lineRenderer.endColor = RGBA_TOCOLOR(rgba);
		Vector3[] positions = new Vector3[n];
		for(int i = 0; i < n; i++) {
			positions[i] = new Vector2(
				(float)xy[i,0],
				(float)xy[i,1]);
		}
		lo.lineRenderer.SetPositions(positions);
		lo.lineRenderer.loop = true;
		lo.lineRenderer.material = Graph.materialPool.GetMaterial(null, Color.white);
	}
	public static void CbkQue_draw_polygon(ST_CbkQue pCbkQue, int pri, double[,] xy, int rgba = -1) {
		CbkQue_Add(pCbkQue, pri, delegate(ST_CbkQue _pCbkQue, int _pri, object param) {
				draw_polygon(xy, rgba);
			}, null);
	}
	private static void draw_polygon_fill(double[,] xy, int rgba = -1) {
		PolygonObject po = Graph.polygonObjectPool.GetObject();
		{
			Mesh mesh = po.meshFilter.mesh;
			int n = xy.GetLength(0);
			if(n < 3) { throw new ApplicationException(); }
			{
				Vector3[] vertices = new Vector3[n];
				for(int i = 0; i < n; i++) { vertices[i] = new Vector2((float)xy[i,0], (float)xy[i,1]); }
				mesh.vertices = vertices;
			}
			{
				int[] triangles = new int[(n - 2) * 3];
				for(int i = 0; i < n - 2; i++) {
					triangles[(i * 3) + 0] = 0;
					triangles[(i * 3) + 1] = i + 1;
					triangles[(i * 3) + 2] = i + 2;
				}
				mesh.triangles = triangles;
			}
#if     !USE_COLOR_MATERIAL
			{
				Color32 c = RGBA_TOCOLOR(rgba);
				Color32[] colors32 = new Color32[n];
				for(int i = 0; i < n; i++) { colors32[i] = c; }
				mesh.colors32 = colors32;
			}
#endif
			po.meshFilter.mesh = mesh;
		}
#if     USE_COLOR_MATERIAL
		po.meshRenderer.material = Graph.materialPool.GetMaterial(null, RGBA_TOCOLOR(rgba));
#else
		po.meshRenderer.material = Graph.materialPool.GetMaterial(null, Color.white);
#endif
	}
	public static void CbkQue_draw_polygon_fill(ST_CbkQue pCbkQue, int pri, double[,] xy, int rgba = -1) {
		CbkQue_Add(pCbkQue, pri, delegate(ST_CbkQue _pCbkQue, int _pri, object param) {
				draw_polygon_fill(xy, rgba);
			}, null);
	}
}
public static partial class Program {
	public static class _Input {
		public static bool		mouseStat, mouseTrig;
		public static int		mousePosX, mousePosY;
		public static BitArray		keyStat, keyTrig;
	}
	public static void Input_Init() {
		{
		}
		{
			int n = (int)Enumerable.Max(TBL_Input_KeyCode) + 1;
			_Input.keyStat = new BitArray(n);
			_Input.keyTrig = new BitArray(n);
		}
	}
	public static void Input_Exit() {
	}
	public static void Input_Exec() {
		{
			if(Application.platform == RuntimePlatform.Android) {
				if(Input.touchCount != 0) {
					Touch touch = Input.GetTouch(0);
					TouchPhase phase = touch.phase;
					switch(phase) {
					default:throw new ApplicationException();
					case TouchPhase.Began:
					case TouchPhase.Moved:
					case TouchPhase.Stationary:
						Vector2 position = touch.position;
						if(Camera.main != null) { position = Camera.main.ScreenToWorldPoint(position); }
						_Input.mousePosX = (int)((position.x * PPU) + (DISP_X / 2));
						_Input.mousePosY = (int)((DISP_Y / 2) - (position.y * PPU));
						_Input.mouseTrig = !_Input.mouseStat;
						_Input.mouseStat = true;
						break;
					case TouchPhase.Ended:
					case TouchPhase.Canceled:
						_Input.mouseStat = false;
						_Input.mouseTrig = false;
						break;
					}
				} else {
					_Input.mouseStat = false;
					_Input.mouseTrig = false;
				}
			} else {
				if(Input.GetMouseButton(0)) {
					Vector2 position = Input.mousePosition;
					if(Camera.main != null) { position = Camera.main.ScreenToWorldPoint(position); }
					_Input.mousePosX = (int)((position.x * PPU) + (DISP_X / 2));
					_Input.mousePosY = (int)((DISP_Y / 2) - (position.y * PPU));
					_Input.mouseTrig = !_Input.mouseStat;
					_Input.mouseStat = true;
				} else {
					_Input.mouseStat = false;
					_Input.mouseTrig = false;
				}
			}
		}
		{
			foreach(KeyCode i in TBL_Input_KeyCode) {
				bool bStat = Input.GetKey(i);
				_Input.keyTrig[(int)i] = bStat && !_Input.keyStat[(int)i];
				_Input.keyStat[(int)i] = bStat;
			}
		}
	}
	private static readonly KeyCode[] TBL_Input_KeyCode={
		KeyCode.Backspace,
		KeyCode.Return,
		KeyCode.Escape,
		KeyCode.Space,
		KeyCode.UpArrow,
		KeyCode.DownArrow,
		KeyCode.RightArrow,
		KeyCode.LeftArrow,
		KeyCode.F1,
		KeyCode.F2,
		KeyCode.F3,
		KeyCode.F4,
		KeyCode.F5,
		KeyCode.F6,
		KeyCode.F7,
		KeyCode.F8,
		KeyCode.F9,
		KeyCode.F10,
		KeyCode.F11,
		KeyCode.F12,
		KeyCode.F13,
		KeyCode.F14,
		KeyCode.F15,
		KeyCode.Alpha0,
		KeyCode.Alpha1,
		KeyCode.Alpha2,
		KeyCode.Alpha3,
		KeyCode.Alpha4,
		KeyCode.Alpha5,
		KeyCode.Alpha6,
		KeyCode.Alpha7,
		KeyCode.Alpha8,
		KeyCode.Alpha9,
		KeyCode.A,
		KeyCode.B,
		KeyCode.C,
		KeyCode.D,
		KeyCode.E,
		KeyCode.F,
		KeyCode.G,
		KeyCode.H,
		KeyCode.I,
		KeyCode.J,
		KeyCode.K,
		KeyCode.L,
		KeyCode.M,
		KeyCode.N,
		KeyCode.O,
		KeyCode.P,
		KeyCode.Q,
		KeyCode.R,
		KeyCode.S,
		KeyCode.T,
		KeyCode.U,
		KeyCode.V,
		KeyCode.W,
		KeyCode.X,
		KeyCode.Y,
		KeyCode.Z,
		KeyCode.Numlock,
		KeyCode.CapsLock,
		KeyCode.ScrollLock,
		KeyCode.RightShift,
		KeyCode.LeftShift,
		KeyCode.RightControl,
		KeyCode.LeftControl,
		KeyCode.RightAlt,
		KeyCode.LeftAlt,
		KeyCode.LeftCommand,
		KeyCode.LeftApple,
		KeyCode.LeftWindows,
		KeyCode.RightCommand,
		KeyCode.RightApple,
		KeyCode.RightWindows,
		KeyCode.AltGr,
		KeyCode.Help,
		KeyCode.Print,
		KeyCode.SysReq,
		KeyCode.Break,
		KeyCode.Menu,
	};
}
public static partial class Program {
	private static class Sound {
		public static ST_AudioHelper		pAudioHelper;
		public static ST_TapSeq			pTapSeq;
		public static ST_TapMxr			pTapMxr;
		public static ST_TapSeqInfo		pTapSeqInfo;
		public static int[][]			TBL_TapMxrChMask;
		public static int			t0;
	}
	public class AudioPlayer : IAudioPlayer {
		CriAtomExPlayer		criAtomExPlayer;
		public AudioPlayer() {
			criAtomExPlayer = new CriAtomExPlayer(260, 1);
		#if     (!UNITY_EDITOR && UNITY_ANDROID)
			criAtomExPlayer.SetSoundRendererType(CriAtomEx.SoundRendererType.Native);
		#endif
		}
		void IDisposable.Dispose() {
			criAtomExPlayer.Dispose();
		}
		AudioPlayerStatus IAudioPlayer.GetStatus() {
			return (AudioPlayerStatus)criAtomExPlayer.GetStatus();
		}
		void IAudioPlayer.Prepare(int iPhrNo) {
			if((iPhrNo < 1) || (iPhrNo > PhrNo_Max)) { throw new ApplicationException(); }
			string path = REG_get_string_l(TBL_RegTbl, RegKey_PhrDef, iPhrNo, RegKey_Path);
			if(path == null) { throw new ApplicationException(); }
			criAtomExPlayer.SetFile(binder, path);
			criAtomExPlayer.Prepare();
		}
		void IAudioPlayer.Start() {
			criAtomExPlayer.Resume(CriAtomEx.ResumeMode.AllPlayback);
		}
		void IAudioPlayer.Stop() {
			criAtomExPlayer.Stop(true);
			for(;;) {
				CriAtomExPlayer.Status status = criAtomExPlayer.GetStatus();
				if(status == CriAtomExPlayer.Status.Error) { throw new ApplicationException(); }
				if(status == CriAtomExPlayer.Status.Stop) { break; }
			}
		}
		void IAudioPlayer.SetVolume(double volume) {
			criAtomExPlayer.SetVolume((float)volume);
			criAtomExPlayer.UpdateAll();
		}
		void IAudioPlayer.SetPan(double pan) {
		}
	}
	public static void Sound_Init(ST_TimMgr pTimMgr, ST_TapSeqInfo pTapSeqInfo, int[][] TBL_TapMxrChMask, out ST_TapSeq pTapSeq, out ST_TapMxr pTapMxr) {
		if(pTapSeqInfo.nDevCh != (TapDevCh_Max+1)) { throw new ApplicationException(); }
		if(pTapSeqInfo.nLogCh != (TapLogCh_Max+1)) { throw new ApplicationException(); }
		if(pTapSeqInfo.nSeqCh != (TapSeqCh_Max+1)) { throw new ApplicationException(); }
		if(TBL_TapMxrChMask.GetLength(0) != (TapLogCh_Max+1)) { throw new ApplicationException(); }
#if     (!UNITY_EDITOR && UNITY_ANDROID)
		int nCh = criWareInitializer.atomConfig.androidLowLatencyStandardVoicePoolConfig.streamingVoices;
#else
		int nCh = criWareInitializer.atomConfig.standardVoicePoolConfig.streamingVoices;
#endif
		if(nCh < pTapSeqInfo.nLogCh) { throw new ApplicationException(); }
		Sound.pAudioHelper = AudioHelper_New(nCh, delegate(object arg) { return new AudioPlayer(); }, null);
		Sound.t0 = Environment.TickCount;
		Sound.pTapSeqInfo = pTapSeqInfo;
		pTapSeqInfo.fnPlay		= Sound_fnPlay;
		pTapSeqInfo.fnStop		= Sound_fnStop;
		pTapSeqInfo.fnLoop		= Sound_fnLoop;
		pTapSeqInfo.fnEnd		= Sound_fnEnd;
		pTapSeqInfo.fnNext		= Sound_fnNext;
		pTapSeqInfo.fnCtrl		= Sound_fnCtrl;
		pTapSeqInfo.fnPrep		= Sound_fnPrep;
		pTapSeqInfo.fnGetPhrTime	= Sound_fnGetPhrTime;
		pTapSeqInfo.fnGetPhrNext	= Sound_fnGetPhrNext;
		pTapSeqInfo.nEvt		= 31;
		pTapSeqInfo.iEvtMsk		= unchecked((byte)~(1<<TapEvt_Ctrl));
		Sound.pTapSeq = TapSeq_New(Sound.pTapSeqInfo);
		pTapSeq = Sound.pTapSeq;
		TimMgr_AddFunc(stTimMgr, TimMgrCh_10ms, new ST_TimMgrFunc() {
				fn = Sound_TapSeq_TimeProc,
				uItv = 1,
			});
		Sound.TBL_TapMxrChMask = TBL_TapMxrChMask;
		Sound.pTapMxr = TapMxr_New(TapMxrCh_Max+1);
		pTapMxr = Sound.pTapMxr;
		TimMgr_AddFunc(stTimMgr, TimMgrCh_10ms, new ST_TimMgrFunc() {
				fn = Sound_TapMxr_TimeProc,
				uItv = 5,
			});
	}
	public static void Sound_Exec() {
		AudioHelper_Exec(Sound.pAudioHelper);
	}
	public static void Sound_Exit() {
		AudioHelper_Exit(Sound.pAudioHelper);
	}
	private static void Sound_fnPlay(ST_TapSeq pTapSeq, int iLogCh, ST_TapLogCh pLogCh) {
		Sound_play_phr(iLogCh, pLogCh);
	}
	private static void Sound_fnStop(ST_TapSeq pTapSeq, int iLogCh, ST_TapLogCh pLogCh) {
		Sound_stop_phr(iLogCh, pLogCh);
	}
	private static void Sound_fnLoop(ST_TapSeq pTapSeq, int iLogCh, ST_TapLogCh pLogCh) {
		Sound_play_phr(iLogCh, pLogCh);
	}
	private static void Sound_fnEnd(ST_TapSeq pTapSeq, int iLogCh, ST_TapLogCh pLogCh) {
		Sound_stop_phr(iLogCh, pLogCh);
	}
	private static void Sound_fnNext(ST_TapSeq pTapSeq, int iLogCh, ST_TapLogCh pLogCh) {
		Sound_play_phr(iLogCh, pLogCh);
	}
	private static void Sound_fnCtrl(ST_TapSeq pTapSeq, int iLogCh, ST_TapLogCh pLogCh) {
		Sound_set_att(iLogCh, pLogCh);
	}
	private static void Sound_fnPrep(ST_TapSeq pTapSeq, int iLogCh, ST_TapLogCh pLogCh) {
		{
			int iPhr = Sound_fnGetPhrNext(pTapSeq, pLogCh.iPhr);
			if(iPhr < 0) {
				if(pLogCh.iLoop != 1) {
					AudioHelper_Prepare(Sound.pAudioHelper, pLogCh.iPhr);
				}
			} else {
				AudioHelper_Prepare(Sound.pAudioHelper, iPhr);
			}
		}
	}
	private static int Sound_fnGetPhrTime(ST_TapSeq pTapSeq, int iPhr) {
		int nTime = REG_get_value_l(
			TBL_RegTbl,
			RegKey_PhrDef,
			iPhr,
			RegKey_Time);
		if(nTime == -1) { throw new ApplicationException(); }
		return nTime;
	}
	private static int Sound_fnGetPhrNext(ST_TapSeq pTapSeq, int iPhr) {
		int iNext = REG_get_value_l(
			TBL_RegTbl,
			RegKey_PhrDef,
			iPhr,
			RegKey_Next);
		iNext = (iNext << 8) >> 8;
		return iNext;
	}
	private static void Sound_TapSeq_TimeProc(object arg) {
		int t1 = Environment.TickCount;
		int dt = t1 - Sound.t0;
		Sound.t0 = t1;
		TapSeq_Exec(Sound.pTapSeq, dt);
	}
	private static void Sound_TapMxr_TimeProc(object arg) {
		TapMxr_Exec(Sound.pTapMxr, (10*5));
		Sound_UpdVol();
	}
	public static void Sound_UpdVol() {
		int iDevCh, iLogCh;
		for(iDevCh = 0; iDevCh < Sound.pTapSeqInfo.nDevCh; iDevCh++) {
			iLogCh = TapSeq_GetDevCh(Sound.pTapSeq, iDevCh).iLogCh;
			if(iLogCh != byte.MaxValue) {
				ST_TapLogCh pLogCh = TapSeq_GetLogCh(Sound.pTapSeq, iLogCh);
				Sound_set_att(iLogCh, pLogCh);
			}
		}
	}
	private static void Sound_play_phr(int iLogCh, ST_TapLogCh pLogCh) {
		int iDevCh = pLogCh.iDevCh;
		if(iDevCh != byte.MaxValue) {
			AudioHelper_Play(Sound.pAudioHelper, iDevCh, pLogCh.iPhr);
			Sound_set_att(iLogCh, pLogCh);
		}
	}
	private static void Sound_stop_phr(int iLogCh, ST_TapLogCh pLogCh) {
		int iDevCh = pLogCh.iDevCh;
		if(iDevCh != byte.MaxValue) {
			AudioHelper_Stop(Sound.pAudioHelper, iDevCh);
		}
	}
	private static void Sound_set_att(int iLogCh, ST_TapLogCh pLogCh) {
		int iDevCh = pLogCh.iDevCh;
		if(iDevCh != byte.MaxValue) {
			int iVol = 255;
			iVol -= TapMxr_GetAtt(Sound.pTapMxr, Sound.TBL_TapMxrChMask[iLogCh]);
			iVol -= pLogCh.iAtt;
			if(iVol < 0) { iVol = 0; }
			AudioHelper_SetVolume(Sound.pAudioHelper, iDevCh, iVol / 255.0);
		}
	}
	public static bool prep_phr(int iPhrNo) {
		return AudioHelper_Prepare(Sound.pAudioHelper, iPhrNo);
	}
}
public static partial class Program {
	public static SToolInfo ToolUnity_GetDefault() {
		return     SToolUnityInfo.GetDefault();
	}
	private class SToolUnityInfo : SToolInfo, ICloneable {
		private static SToolUnityInfo    Default;
		public  static SToolUnityInfo GetDefault() {
			if(Default == null) {
				Default = new SToolUnityInfo() {
					fnDrawBox			= _fnDrawBox,
					fnDrawStringToHandle		= _fnDrawStringToHandle,
					fnDrawTriangle			= _fnDrawTriangle,
					fnGetColor			= _fnGetColor,
					fnGetDrawScreenSize		= _fnGetDrawScreenSize,
					fnGetDrawStringWidthToHandle	= _fnGetDrawStringWidthToHandle,
					fnGetMouseInput			= _fnGetMouseInput,
					fnGetMousePoint			= _fnGetMousePoint,
					fnSetDrawArea			= _fnSetDrawArea,
					fnSetDrawAreaFull		= _fnSetDrawAreaFull,
					fnSetDrawBlendMode		= _fnSetDrawBlendMode,
					NormalFontHandle		= 1,
					ButtonFontHandle		= 2,
				};
				if((Default.ButtonFontHandle == -1) ||
				   (Default.NormalFontHandle == -1)) { throw new ApplicationException(); }
			}
			return (SToolUnityInfo)Default.Clone();
		}
		private const int	BasePriority	= short.MaxValue - 100;
		private const double	FontScale	= TOOL_FONT_HEIGHT / 14.0;
		private static int	BlendParam;
		private static int _fnDrawBox(int x1, int y1, int x2, int y2, uint Color, bool FillFlag) {
			double x = x1, y = y1, w = x2 - x1, h = y2 - y1;
			int rgba = RGBA_SETALPHA((int)Color, BlendParam);
			if(FillFlag) {
				CbkQue_draw_box_fill(pCbkQue, (BasePriority + 0), x, y, w, h, rgba);
			} else {
				CbkQue_draw_box(pCbkQue, (BasePriority + 0), x, y, w, h, rgba);
			}
			return 0;
		}
		private static int _fnDrawStringToHandle(int x, int y, string String, uint Color, int FontHandle) {
			int rgba = (FontHandle == 1) ? RGBA_SETALPHA((int)Color, BlendParam) : -1;
			CbkQue_draw_sprf(pCbkQue, (BasePriority + 1), pSprFntMda, x, y, FontScale, FontScale, 0, 0, 0, rgba, String);
			return 0;
		}
		private static int _fnDrawTriangle(int x1, int y1, int x2, int y2, int x3, int y3, uint Color, bool FillFlag) {
			double[,] xy = new double[,] { { x1, y1 }, { x2, y2 }, { x3, y3 } };
			int rgba = RGBA_SETALPHA((int)Color, BlendParam);
			if(FillFlag) {
				CbkQue_draw_polygon_fill(pCbkQue, (BasePriority + 0), xy, rgba);
			} else {
				CbkQue_draw_polygon(pCbkQue, (BasePriority + 0), xy, rgba);
			}
			return 0;
		}
		private static uint _fnGetColor(int Red, int Green, int Blue) {
			return (uint)RGB_MAKE(Red, Green, Blue);
		}
		private static int _fnGetDrawScreenSize(out int XBuf, out int YBuf) {
			XBuf = DISP_X;
			YBuf = DISP_Y;
			return 0;
		}
		private static int _fnGetDrawStringWidthToHandle(string String, int StrLen, int FontHandle) {
			int size_x, size_y;
			if(StrLen != -1) { throw new ApplicationException(); }
			SprFnt_GetSize(pSprFntMda, String, out size_x, out size_y);
			return (int)(size_x * FontScale);
		}
		private static int _fnGetMouseInput() {
			return _Input.mouseStat ? (1<<0) : 0;
		}
		private static int _fnGetMousePoint(out int XBuf, out int YBuf) {
			XBuf = _Input.mousePosX;
			YBuf = _Input.mousePosY;
			return 0;
		}
		private static int _fnSetDrawArea(int x1, int y1, int x2, int y2) {
			return 0;
		}
		private static int _fnSetDrawAreaFull() {
			return 0;
		}
		private static int _fnSetDrawBlendMode(int BlendMode, int BlendParam) {
			switch(BlendMode) {
			default:throw new ApplicationException();
			case 0:
				SToolUnityInfo.BlendParam = 255;
				break;
			case 1:
				SToolUnityInfo.BlendParam = clamp(BlendParam, 0, 255);
				break;
			}
			return 0;
		}
		public object Clone() { return MemberwiseClone(); }
	}
}
