//------------------------------------------------------------ // Game Framework // Copyright © 2013-2021 loyalsoft. All rights reserved. // Homepage: http://www.game7000.com/ // Feedback: http://www.game7000.com/ //------------------------------------------------------------ using GameFramework; using GameFramework.FileSystem; using System; using System.IO; using UnityEngine; namespace UnityGameFramework.Runtime { /// /// 安卓文件系统流。 /// public sealed class AndroidFileSystemStream : FileSystemStream { private static readonly string SplitFlag = "!/assets/"; private static readonly int SplitFlagLength = SplitFlag.Length; private static readonly AndroidJavaObject s_AssetManager = null; private static readonly IntPtr s_InternalReadMethodId = IntPtr.Zero; private static readonly jvalue[] s_InternalReadArgs = null; private readonly AndroidJavaObject m_FileStream; private readonly IntPtr m_FileStreamRawObject; static AndroidFileSystemStream() { AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer"); if (unityPlayer == null) { throw new GameFrameworkException("Unity player is invalid."); } AndroidJavaObject currentActivity = unityPlayer.GetStatic("currentActivity"); if (currentActivity == null) { throw new GameFrameworkException("Current activity is invalid."); } AndroidJavaObject assetManager = currentActivity.Call("getAssets"); if (assetManager == null) { throw new GameFrameworkException("Asset manager is invalid."); } s_AssetManager = assetManager; IntPtr inputStreamClassPtr = AndroidJNI.FindClass("java/io/InputStream"); s_InternalReadMethodId = AndroidJNIHelper.GetMethodID(inputStreamClassPtr, "read", "([BII)I"); s_InternalReadArgs = new jvalue[3]; AndroidJNI.DeleteLocalRef(inputStreamClassPtr); currentActivity.Dispose(); unityPlayer.Dispose(); } /// /// 初始化安卓文件系统流的新实例。 /// /// 要加载的文件系统的完整路径。 /// 要加载的文件系统的访问方式。 /// 是否创建新的文件系统流。 public AndroidFileSystemStream(string fullPath, FileSystemAccess access, bool createNew) { if (string.IsNullOrEmpty(fullPath)) { throw new GameFrameworkException("Full path is invalid."); } if (access != FileSystemAccess.Read) { throw new GameFrameworkException(Utility.Text.Format("'{0}' is not supported in AndroidFileSystemStream.", access.ToString())); } if (createNew) { throw new GameFrameworkException("Create new is not supported in AndroidFileSystemStream."); } int position = fullPath.LastIndexOf(SplitFlag, StringComparison.Ordinal); if (position < 0) { throw new GameFrameworkException("Can not find split flag in full path."); } string fileName = fullPath.Substring(position + SplitFlagLength); m_FileStream = InternalOpen(fileName); if (m_FileStream == null) { throw new GameFrameworkException(Utility.Text.Format("Open file '{0}' from Android asset manager failure.", fullPath)); } m_FileStreamRawObject = m_FileStream.GetRawObject(); } /// /// 获取或设置文件系统流位置。 /// public override long Position { get { throw new GameFrameworkException("Get position is not supported in AndroidFileSystemStream."); } set { Seek(value, SeekOrigin.Begin); } } /// /// 获取文件系统流长度。 /// public override long Length { get { return InternalAvailable(); } } /// /// 设置文件系统流长度。 /// /// 要设置的文件系统流的长度。 public override void SetLength(long length) { throw new GameFrameworkException("SetLength is not supported in AndroidFileSystemStream."); } /// /// 定位文件系统流位置。 /// /// 要定位的文件系统流位置的偏移。 /// 要定位的文件系统流位置的方式。 public override void Seek(long offset, SeekOrigin origin) { if (origin == SeekOrigin.End) { Seek(Length + offset, SeekOrigin.Begin); return; } if (origin == SeekOrigin.Begin) { InternalReset(); } while (offset > 0) { long skip = InternalSkip(offset); if (skip < 0) { return; } offset -= skip; } } /// /// 从文件系统流中读取一个字节。 /// /// 读取的字节,若已经到达文件结尾,则返回 -1。 public override int ReadByte() { return InternalRead(); } /// /// 从文件系统流中读取二进制流。 /// /// 存储读取文件内容的二进制流。 /// 存储读取文件内容的二进制流的起始位置。 /// 存储读取文件内容的二进制流的长度。 /// 实际读取了多少字节。 public override int Read(byte[] buffer, int startIndex, int length) { byte[] result = null; int bytesRead = InternalRead(length, out result); Array.Copy(result, 0, buffer, startIndex, bytesRead); return bytesRead; } /// /// 向文件系统流中写入一个字节。 /// /// 要写入的字节。 public override void WriteByte(byte value) { throw new GameFrameworkException("WriteByte is not supported in AndroidFileSystemStream."); } /// /// 向文件系统流中写入二进制流。 /// /// 存储写入文件内容的二进制流。 /// 存储写入文件内容的二进制流的起始位置。 /// 存储写入文件内容的二进制流的长度。 public override void Write(byte[] buffer, int startIndex, int length) { throw new GameFrameworkException("Write is not supported in AndroidFileSystemStream."); } /// /// 将文件系统流立刻更新到存储介质中。 /// public override void Flush() { throw new GameFrameworkException("Flush is not supported in AndroidFileSystemStream."); } /// /// 关闭文件系统流。 /// public override void Close() { InternalClose(); m_FileStream.Dispose(); } private AndroidJavaObject InternalOpen(string fileName) { return s_AssetManager.Call("open", fileName); } private int InternalAvailable() { return m_FileStream.Call("available"); } private void InternalClose() { m_FileStream.Call("close"); } private int InternalRead() { return m_FileStream.Call("read"); } private int InternalRead(int length, out byte[] result) { #if UNITY_2019_2_OR_NEWER #pragma warning disable CS0618 #endif IntPtr resultPtr = AndroidJNI.NewByteArray(length); #if UNITY_2019_2_OR_NEWER #pragma warning restore CS0618 #endif int offset = 0; int bytesLeft = length; while (bytesLeft > 0) { s_InternalReadArgs[0] = new jvalue() { l = resultPtr }; s_InternalReadArgs[1] = new jvalue() { i = offset }; s_InternalReadArgs[2] = new jvalue() { i = bytesLeft }; int bytesRead = AndroidJNI.CallIntMethod(m_FileStreamRawObject, s_InternalReadMethodId, s_InternalReadArgs); if (bytesRead <= 0) { break; } offset += bytesRead; bytesLeft -= bytesRead; } #if UNITY_2019_2_OR_NEWER #pragma warning disable CS0618 #endif result = AndroidJNI.FromByteArray(resultPtr); #if UNITY_2019_2_OR_NEWER #pragma warning restore CS0618 #endif AndroidJNI.DeleteLocalRef(resultPtr); return offset; } private void InternalReset() { m_FileStream.Call("reset"); } private long InternalSkip(long offset) { return m_FileStream.Call("skip", offset); } } }