Update samples

pull/51/head
hevinci 2022-10-08 12:10:34 +08:00
parent 38c0ca0ce1
commit 852d031254
27 changed files with 1941 additions and 773 deletions

View File

@ -2,13 +2,14 @@
using System.Collections; using System.Collections;
using UnityEngine; using UnityEngine;
using YooAsset; using YooAsset;
using Better.StreamingAssets;
public class BootScene : MonoBehaviour public class BootScene : MonoBehaviour
{ {
public static BootScene Instance { private set; get; } public static BootScene Instance { private set; get; }
public static YooAssets.EPlayMode GamePlayMode; public static EPlayMode GamePlayMode;
public YooAssets.EPlayMode PlayMode = YooAssets.EPlayMode.EditorSimulateMode; public EPlayMode PlayMode = EPlayMode.EditorSimulateMode;
void Awake() void Awake()
{ {
@ -36,36 +37,37 @@ public class BootScene : MonoBehaviour
GamePlayMode = PlayMode; GamePlayMode = PlayMode;
Debug.Log($"资源系统运行模式:{PlayMode}"); Debug.Log($"资源系统运行模式:{PlayMode}");
// 初始化BetterStreaming
BetterStreamingAssets.Initialize();
// 初始化资源系统
YooAssets.Initialize();
// 编辑器下的模拟模式 // 编辑器下的模拟模式
if (PlayMode == YooAssets.EPlayMode.EditorSimulateMode) if (PlayMode == EPlayMode.EditorSimulateMode)
{ {
var createParameters = new YooAssets.EditorSimulateModeParameters(); var createParameters = new EditorSimulateModeParameters();
createParameters.LocationServices = new AddressLocationServices(); createParameters.LocationServices = new AddressLocationServices();
createParameters.BuildinPackageName = "DefaultPackage"; createParameters.SimulatePatchManifestPath = EditorSimulateModeHelper.SimulateBuild("DefaultPackage", true);
//createParameters.SimulatePatchManifestPath = GetPatchManifestPath();
yield return YooAssets.InitializeAsync(createParameters); yield return YooAssets.InitializeAsync(createParameters);
} }
// 单机运行模式 // 单机运行模式
if (PlayMode == YooAssets.EPlayMode.OfflinePlayMode) if (PlayMode == EPlayMode.OfflinePlayMode)
{ {
var createParameters = new YooAssets.OfflinePlayModeParameters(); var createParameters = new OfflinePlayModeParameters();
createParameters.LocationServices = new AddressLocationServices(); createParameters.LocationServices = new AddressLocationServices();
createParameters.BuildinPackageName = "DefaultPackage";
yield return YooAssets.InitializeAsync(createParameters); yield return YooAssets.InitializeAsync(createParameters);
} }
// 联机运行模式 // 联机运行模式
if (PlayMode == YooAssets.EPlayMode.HostPlayMode) if (PlayMode == EPlayMode.HostPlayMode)
{ {
var createParameters = new YooAssets.HostPlayModeParameters(); var createParameters = new HostPlayModeParameters();
createParameters.LocationServices = new AddressLocationServices(); createParameters.LocationServices = new AddressLocationServices();
createParameters.BuildinPackageName = "DefaultPackage"; createParameters.QueryServices = new QueryStreamingAssetsFileServices();
createParameters.DecryptionServices = null;
createParameters.ClearCacheWhenDirty = false;
createParameters.DefaultHostServer = GetHostServerURL(); createParameters.DefaultHostServer = GetHostServerURL();
createParameters.FallbackHostServer = GetHostServerURL(); createParameters.FallbackHostServer = GetHostServerURL();
createParameters.VerifyLevel = EVerifyLevel.High;
yield return YooAssets.InitializeAsync(createParameters); yield return YooAssets.InitializeAsync(createParameters);
} }
@ -73,11 +75,6 @@ public class BootScene : MonoBehaviour
PatchUpdater.Run(); PatchUpdater.Run();
} }
private string GetPatchManifestPath()
{
string directory = System.IO.Path.GetDirectoryName(Application.dataPath);
return $"{directory}/Bundles/StandaloneWindows64/UnityManifest_SimulateBuild/PatchManifest_100.bytes";
}
private string GetHostServerURL() private string GetHostServerURL()
{ {
//string hostServerIP = "http://10.0.2.2"; //安卓模拟器地址 //string hostServerIP = "http://10.0.2.2"; //安卓模拟器地址
@ -104,4 +101,11 @@ public class BootScene : MonoBehaviour
return $"{hostServerIP}/CDN/PC/{gameVersion}"; return $"{hostServerIP}/CDN/PC/{gameVersion}";
#endif #endif
} }
private class QueryStreamingAssetsFileServices : IQueryServices
{
public bool QueryStreamingAssets(string fileName)
{
return BetterStreamingAssets.FileExists($"YooAssets/{fileName}");
}
}
} }

View File

@ -46,11 +46,11 @@ public class GameScene1 : MonoBehaviour
void InitWindow() void InitWindow()
{ {
var playMode = CanvasRoot.transform.Find("play_mode/label").GetComponent<Text>(); var playMode = CanvasRoot.transform.Find("play_mode/label").GetComponent<Text>();
if (BootScene.GamePlayMode == YooAssets.EPlayMode.EditorSimulateMode) if (BootScene.GamePlayMode == EPlayMode.EditorSimulateMode)
playMode.text = "编辑器下模拟模式"; playMode.text = "编辑器下模拟模式";
else if (BootScene.GamePlayMode == YooAssets.EPlayMode.OfflinePlayMode) else if (BootScene.GamePlayMode == EPlayMode.OfflinePlayMode)
playMode.text = "离线运行模式"; playMode.text = "离线运行模式";
else if (BootScene.GamePlayMode == YooAssets.EPlayMode.HostPlayMode) else if (BootScene.GamePlayMode == EPlayMode.HostPlayMode)
playMode.text = "网络运行模式"; playMode.text = "网络运行模式";
else else
throw new NotImplementedException(); throw new NotImplementedException();

View File

@ -24,7 +24,7 @@ public class FsmUpdateManifest : IFsmNode
yield return new WaitForSecondsRealtime(0.5f); yield return new WaitForSecondsRealtime(0.5f);
// 更新补丁清单 // 更新补丁清单
var operation = YooAssets.UpdateManifestAsync("DefaultPackage", PatchUpdater.PackageCRC, 30); var operation = YooAssets.UpdateManifestAsync(PatchUpdater.PackageCRC, 30);
yield return operation; yield return operation;
if(operation.Status == EOperationStatus.Succeed) if(operation.Status == EOperationStatus.Succeed)

View File

@ -24,7 +24,7 @@ internal class FsmUpdateStaticVersion : IFsmNode
yield return new WaitForSecondsRealtime(0.5f); yield return new WaitForSecondsRealtime(0.5f);
// 更新资源版本号 // 更新资源版本号
var operation = YooAssets.UpdateStaticVersionAsync("DefaultPackage", 30); var operation = YooAssets.UpdateStaticVersionAsync(30);
yield return operation; yield return operation;
if (operation.Status == EOperationStatus.Succeed) if (operation.Status == EOperationStatus.Succeed)

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: 6f807a00a11cdd24490fd433a55ecd4f guid: e214524cdc329d646b50bf3e8ba8e7a0
folderAsset: yes folderAsset: yes
DefaultImporter: DefaultImporter:
externalObjects: {} externalObjects: {}

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: 22d47ea6e32e3c64c8463e1f0fa7506a guid: 89357359fd1b3f74fa7cb048a1ffa2b6
folderAsset: yes folderAsset: yes
DefaultImporter: DefaultImporter:
externalObjects: {} externalObjects: {}

View File

@ -0,0 +1,34 @@
/[Ll]ibrary/
/[Tt]emp/
/[Oo]bj/
/[Bb]uild/
/[Bb]uilds/
/Assets/AssetStoreTools*
# Visual Studio 2015 cache directory
/.vs/
# Autogenerated VS/MD/Consulo solution and project files
ExportedObj/
.consulo/
*.csproj
*.unityproj
*.sln
*.suo
*.tmp
*.user
*.userprefs
*.pidb
*.booproj
*.svd
*.pdb
# Unity3D generated meta files
*.pidb.meta
# Unity3D Generated File On Crash Reports
sysinfo.txt
# Builds
*.apk
*.unitypackage

View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2017 gwiazdorrr
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: 528b2874f5351ca4894d885979739c4d guid: f3d9e435137e34d34a54f181bf3dc7c2
DefaultImporter: DefaultImporter:
externalObjects: {} externalObjects: {}
userData: userData:

View File

@ -0,0 +1,117 @@
# Better Streaming Assets
Better Streaming Assets is a plugin that lets you access Streaming Assets directly in an uniform and thread-safe way, with tiny overhead. Mostly beneficial for Android projects, where the alternatives are to use archaic and hugely inefficient WWW or embed data in Asset Bundles. API is based on Syste.IO.File and System.IO.Directory classes.
# Note on Android & App Bundles
App Bundles (.aab) builds are bugged when it comes to Streaming Assets. See https://github.com/gwiazdorrr/BetterStreamingAssets/issues/10 for details. The bottom line is:
⚠️ **Keep all file names in Streaming Assets lowercase!** ⚠️
# Getting started
This plugin can be installed in following ways:
* Select "Add package from git URL..." in the Unity Package Manager and use this URL: `https://github.com/gwiazdorrr/BetterStreamingAssets.git`
* Clone this repository and copy `Runtime` directory to your project.
* Download the latest release from the [Asset Store](https://assetstore.unity.com/packages/tools/input-management/better-streaming-assets-103788).
# Usage
Check examples below. Note that all the paths are relative to StreamingAssets directory. That is, if you have files
```
<project>/Assets/StreamingAssets/foo.bar
<project>/Assets/StreamingAssets/dir/foo.bar
````
You are expected to use following paths:
```
foo.bar (or /foo.bar)
dir/foo.bar (or /dir/foo.bar)
```
# Examples
Initialization (before first use, needs to be called on main thread):
```csharp
BetterStreamingAssets.Initialize();
```
Typical scenario, deserializing from Xml:
```csharp
public static Foo ReadFromXml(string path)
{
if ( !BetterStreamingAssets.FileExists(path) )
{
Debug.LogErrorFormat("Streaming asset not found: {0}", path);
return null;
}
using ( var stream = BetterStreamingAssets.OpenRead(path) )
{
var serializer = new System.Xml.Serialization.XmlSerializer(typeof(Foo));
return (Foo)serializer.Deserialize(stream);
}
}
```
Note that ReadFromXml can be called from any thread, as long as Foo's constructor doesn't make any UnityEngine calls.
Listing all Streaming Assets in with .xml extension:
```csharp
// all the xmls
string[] paths = BetterStreamingAssets.GetFiles("\\", "*.xml", SearchOption.AllDirectories);
// just xmls in Config directory (and nested)
string[] paths = BetterStreamingAssets.GetFiles("Config", "*.xml", SearchOption.AllDirectories);
```
Checking if a directory exists:
```csharp
Debug.Assert( BetterStreamingAssets.DirectoryExists("Config") );
```
Ways of reading a file:
```csharp
// all at once
byte[] data = BetterStreamingAssets.ReadAllBytes("Foo/bar.data");
// as stream, last 10 bytes
byte[] footer = new byte[10];
using (var stream = BetterStreamingAssets.OpenRead("Foo/bar.data"))
{
stream.Seek(-footer.Length, SeekOrigin.End);
stream.Read(footer, 0, footer.Length);
}
```
Asset bundles (again, main thread only):
```csharp
// synchronous
var bundle = BetterStreamingAssets.LoadAssetBundle(path);
// async
var bundleOp = BetterStreamingAssets.LoadAssetBundleAsync(path);
```
# (Android) False-positive compressed Streaming Assets messages
Streaming Assets end up in the same part of APK as files added by many custom plugins (`assets` directory), so it is impossible to tell whether a compressed file is a Streaming Asset (an indication something has gone terribly wrong) or not. This tool acts conservatively and logs errors whenever it finds a compressed file inside of `assets`, but outside of `assets/bin`. If you are annoyed by this and are certain a compressed file was not meant to be a Streaming Asset, add a file like this in the same assembly as Better Streaming Assets:
```csharp
partial class BetterStreamingAssets
{
static partial void AndroidIsCompressedFileStreamingAsset(string path, ref bool result)
{
if ( path == "assets/my_custom_plugin_settings.json")
{
result = false;
}
}
}
```

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: c59a049ac5cdb4a48996edcefd9ac9c7
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 89bc753a71211472587f0baa7a08d0e7
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,663 @@
// Better Streaming Assets, Piotr Gwiazdowski <gwiazdorrr+github at gmail.com>, 2017
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using System.IO;
using System.Text.RegularExpressions;
using Better;
using Better.StreamingAssets;
using Better.StreamingAssets.ZipArchive;
#if UNITY_EDITOR
using BetterStreamingAssetsImp = BetterStreamingAssets.EditorImpl;
#elif UNITY_ANDROID
using BetterStreamingAssetsImp = BetterStreamingAssets.ApkImpl;
#else
using BetterStreamingAssetsImp = BetterStreamingAssets.LooseFilesImpl;
#endif
public static partial class BetterStreamingAssets
{
internal struct ReadInfo
{
public string readPath;
public long size;
public long offset;
public uint crc32;
}
public static string Root
{
get { return BetterStreamingAssetsImp.s_root; }
}
public static void Initialize()
{
BetterStreamingAssetsImp.Initialize(Application.dataPath, Application.streamingAssetsPath);
}
#if UNITY_EDITOR
public static void InitializeWithExternalApk(string apkPath)
{
BetterStreamingAssetsImp.ApkMode = true;
BetterStreamingAssetsImp.Initialize(apkPath, "jar:file://" + apkPath + "!/assets/");
}
public static void InitializeWithExternalDirectories(string dataPath, string streamingAssetsPath)
{
BetterStreamingAssetsImp.ApkMode = false;
BetterStreamingAssetsImp.Initialize(dataPath, streamingAssetsPath);
}
#endif
public static bool FileExists(string path)
{
ReadInfo info;
return BetterStreamingAssetsImp.TryGetInfo(path, out info);
}
public static bool DirectoryExists(string path)
{
return BetterStreamingAssetsImp.DirectoryExists(path);
}
public static AssetBundleCreateRequest LoadAssetBundleAsync(string path, uint crc = 0)
{
var info = GetInfoOrThrow(path);
return AssetBundle.LoadFromFileAsync(info.readPath, crc, (ulong)info.offset);
}
public static AssetBundle LoadAssetBundle(string path, uint crc = 0)
{
var info = GetInfoOrThrow(path);
return AssetBundle.LoadFromFile(info.readPath, crc, (ulong)info.offset);
}
public static System.IO.Stream OpenRead(string path)
{
if ( path == null )
throw new ArgumentNullException("path");
if ( path.Length == 0 )
throw new ArgumentException("Empty path", "path");
return BetterStreamingAssetsImp.OpenRead(path);
}
public static System.IO.StreamReader OpenText(string path)
{
Stream str = OpenRead(path);
try
{
return new StreamReader(str);
}
catch (System.Exception)
{
if (str != null)
str.Dispose();
throw;
}
}
public static string ReadAllText(string path)
{
using ( var sr = OpenText(path) )
{
return sr.ReadToEnd();
}
}
public static string[] ReadAllLines(string path)
{
string line;
var lines = new List<string>();
using ( var sr = OpenText(path) )
{
while ( ( line = sr.ReadLine() ) != null )
{
lines.Add(line);
}
}
return lines.ToArray();
}
public static byte[] ReadAllBytes(string path)
{
if ( path == null )
throw new ArgumentNullException("path");
if ( path.Length == 0 )
throw new ArgumentException("Empty path", "path");
return BetterStreamingAssetsImp.ReadAllBytes(path);
}
public static string[] GetFiles(string path, string searchPattern, SearchOption searchOption)
{
return BetterStreamingAssetsImp.GetFiles(path, searchPattern, searchOption);
}
public static string[] GetFiles(string path)
{
return GetFiles(path, null);
}
public static string[] GetFiles(string path, string searchPattern)
{
return GetFiles(path, searchPattern, SearchOption.TopDirectoryOnly);
}
private static ReadInfo GetInfoOrThrow(string path)
{
ReadInfo result;
if ( !BetterStreamingAssetsImp.TryGetInfo(path, out result) )
ThrowFileNotFound(path);
return result;
}
private static void ThrowFileNotFound(string path)
{
throw new FileNotFoundException("File not found", path);
}
static partial void AndroidIsCompressedFileStreamingAsset(string path, ref bool result);
#if UNITY_EDITOR
internal static class EditorImpl
{
public static bool ApkMode = false;
public static string s_root
{
get { return ApkMode ? ApkImpl.s_root : LooseFilesImpl.s_root; }
}
internal static void Initialize(string dataPath, string streamingAssetsPath)
{
if ( ApkMode )
{
ApkImpl.Initialize(dataPath, streamingAssetsPath);
}
else
{
LooseFilesImpl.Initialize(dataPath, streamingAssetsPath);
}
}
internal static bool TryGetInfo(string path, out ReadInfo info)
{
if ( ApkMode )
return ApkImpl.TryGetInfo(path, out info);
else
return LooseFilesImpl.TryGetInfo(path, out info);
}
internal static bool DirectoryExists(string path)
{
if ( ApkMode )
return ApkImpl.DirectoryExists(path);
else
return LooseFilesImpl.DirectoryExists(path);
}
internal static Stream OpenRead(string path)
{
if ( ApkMode )
return ApkImpl.OpenRead(path);
else
return LooseFilesImpl.OpenRead(path);
}
internal static byte[] ReadAllBytes(string path)
{
if ( ApkMode )
return ApkImpl.ReadAllBytes(path);
else
return LooseFilesImpl.ReadAllBytes(path);
}
internal static string[] GetFiles(string path, string searchPattern, SearchOption searchOption)
{
if ( ApkMode )
return ApkImpl.GetFiles(path, searchPattern, searchOption);
else
return LooseFilesImpl.GetFiles(path, searchPattern, searchOption);
}
}
#endif
#if UNITY_EDITOR || !UNITY_ANDROID
internal static class LooseFilesImpl
{
public static string s_root;
private static string[] s_emptyArray = new string[0];
public static void Initialize(string dataPath, string streamingAssetsPath)
{
s_root = Path.GetFullPath(streamingAssetsPath).Replace('\\', '/').TrimEnd('/');
}
public static string[] GetFiles(string path, string searchPattern, SearchOption searchOption)
{
if (!Directory.Exists(s_root))
return s_emptyArray;
// this will throw if something is fishy
path = PathUtil.NormalizeRelativePath(path, forceTrailingSlash : true);
Debug.Assert(s_root.Last() != '\\' && s_root.Last() != '/' && path.StartsWith("/"));
var files = Directory.GetFiles(s_root + path, searchPattern ?? "*", searchOption);
for ( int i = 0; i < files.Length; ++i )
{
Debug.Assert(files[i].StartsWith(s_root));
files[i] = files[i].Substring(s_root.Length + 1).Replace('\\', '/');
}
#if UNITY_EDITOR
// purge meta files
{
int j = 0;
for ( int i = 0; i < files.Length; ++i )
{
if ( !files[i].EndsWith(".meta") )
{
files[j++] = files[i];
}
}
Array.Resize(ref files, j);
}
#endif
return files;
}
public static bool TryGetInfo(string path, out ReadInfo info)
{
path = PathUtil.NormalizeRelativePath(path);
info = new ReadInfo();
var fullPath = s_root + path;
if ( !File.Exists(fullPath) )
return false;
info.readPath = fullPath;
return true;
}
public static bool DirectoryExists(string path)
{
var normalized = PathUtil.NormalizeRelativePath(path);
return Directory.Exists(s_root + normalized);
}
public static byte[] ReadAllBytes(string path)
{
ReadInfo info;
if ( !TryGetInfo(path, out info) )
ThrowFileNotFound(path);
return File.ReadAllBytes(info.readPath);
}
public static System.IO.Stream OpenRead(string path)
{
ReadInfo info;
if ( !TryGetInfo(path, out info) )
ThrowFileNotFound(path);
Stream fileStream = File.OpenRead(info.readPath);
try
{
return new SubReadOnlyStream(fileStream, leaveOpen: false);
}
catch ( System.Exception )
{
fileStream.Dispose();
throw;
}
}
}
#endif
#if UNITY_EDITOR || UNITY_ANDROID
internal static class ApkImpl
{
private static string[] s_paths;
private static PartInfo[] s_streamingAssets;
public static string s_root;
private struct PartInfo
{
public long size;
public long offset;
public uint crc32;
}
public static void Initialize(string dataPath, string streamingAssetsPath)
{
s_root = dataPath;
List<string> paths = new List<string>();
List<PartInfo> parts = new List<PartInfo>();
GetStreamingAssetsInfoFromJar(s_root, paths, parts);
if (paths.Count == 0 && !Application.isEditor && Path.GetFileName(dataPath) != "base.apk")
{
// maybe split?
var newDataPath = Path.GetDirectoryName(dataPath) + "/base.apk";
if (File.Exists(newDataPath))
{
s_root = newDataPath;
GetStreamingAssetsInfoFromJar(newDataPath, paths, parts);
}
}
s_paths = paths.ToArray();
s_streamingAssets = parts.ToArray();
}
public static bool TryGetInfo(string path, out ReadInfo info)
{
path = PathUtil.NormalizeRelativePath(path);
info = new ReadInfo();
var index = Array.BinarySearch(s_paths, path, StringComparer.OrdinalIgnoreCase);
if ( index < 0 )
return false;
var dataInfo = s_streamingAssets[index];
info.crc32 = dataInfo.crc32;
info.offset = dataInfo.offset;
info.size = dataInfo.size;
info.readPath = s_root;
return true;
}
public static bool DirectoryExists(string path)
{
var normalized = PathUtil.NormalizeRelativePath(path, forceTrailingSlash : true);
var dirIndex = GetDirectoryIndex(normalized);
return dirIndex >= 0 && dirIndex < s_paths.Length;
}
public static string[] GetFiles(string path, string searchPattern, SearchOption searchOption)
{
if ( path == null )
throw new ArgumentNullException("path");
var actualDirPath = PathUtil.NormalizeRelativePath(path, forceTrailingSlash : true);
// find first file there
var index = GetDirectoryIndex(actualDirPath);
if ( index < 0 )
throw new IOException();
if ( index == s_paths.Length )
throw new DirectoryNotFoundException();
Predicate<string> filter;
if ( string.IsNullOrEmpty(searchPattern) || searchPattern == "*" )
{
filter = null;
}
else if ( searchPattern.IndexOf('*') >= 0 || searchPattern.IndexOf('?') >= 0 )
{
var regex = PathUtil.WildcardToRegex(searchPattern);
filter = (x) => regex.IsMatch(x);
}
else
{
filter = (x) => string.Compare(x, searchPattern, true) == 0;
}
List<string> results = new List<string>();
string fixedPath = null;
for ( int i = index; i < s_paths.Length; ++i )
{
var filePath = s_paths[i];
if ( !filePath.StartsWith(actualDirPath) )
break;
string fileName;
var dirSeparatorIndex = filePath.LastIndexOf('/', filePath.Length - 1, filePath.Length - actualDirPath.Length);
if ( dirSeparatorIndex >= 0 )
{
if ( searchOption == SearchOption.TopDirectoryOnly )
continue;
fileName = filePath.Substring(dirSeparatorIndex + 1);
}
else
{
fileName = filePath.Substring(actualDirPath.Length);
}
// now do a match
if ( filter == null || filter(fileName) )
{
var normalizedPart = filePath.Substring(actualDirPath.Length);
if ( fixedPath == null )
{
fixedPath = PathUtil.FixTrailingDirectorySeparators(path);
if ( fixedPath == "/" )
fixedPath = string.Empty;
}
var result = PathUtil.CombineSlash(fixedPath, normalizedPart);
results.Add(result);
}
}
return results.ToArray();
}
public static byte[] ReadAllBytes(string path)
{
ReadInfo info;
if ( !TryGetInfo(path, out info) )
ThrowFileNotFound(path);
byte[] buffer;
using ( var fileStream = File.OpenRead(info.readPath) )
{
if ( info.offset != 0 )
{
if ( fileStream.Seek(info.offset, SeekOrigin.Begin) != info.offset )
throw new IOException();
}
if ( info.size > (long)int.MaxValue )
throw new IOException();
int count = (int)info.size;
int offset = 0;
buffer = new byte[count];
while ( count > 0 )
{
int num = fileStream.Read(buffer, offset, count);
if ( num == 0 )
throw new EndOfStreamException();
offset += num;
count -= num;
}
}
return buffer;
}
public static System.IO.Stream OpenRead(string path)
{
ReadInfo info;
if ( !TryGetInfo(path, out info) )
ThrowFileNotFound(path);
Stream fileStream = File.OpenRead(info.readPath);
try
{
return new SubReadOnlyStream(fileStream, info.offset, info.size, leaveOpen : false);
}
catch ( System.Exception )
{
fileStream.Dispose();
throw;
}
}
private static int GetDirectoryIndex(string path)
{
Debug.Assert(s_paths != null);
// find first file there
var index = Array.BinarySearch(s_paths, path, StringComparer.OrdinalIgnoreCase);
if ( index >= 0 )
return ~index;
// if the end, no such directory exists
index = ~index;
if ( index == s_paths.Length )
return index;
for ( int i = index; i < s_paths.Length && s_paths[i].StartsWith(path); ++i )
{
// because otherwise there would be a match
Debug.Assert(s_paths[i].Length > path.Length);
if ( path[path.Length - 1] == '/' )
return i;
if ( s_paths[i][path.Length] == '/' )
return i;
}
return s_paths.Length;
}
private static void GetStreamingAssetsInfoFromJar(string apkPath, List<string> paths, List<PartInfo> parts)
{
using ( var stream = File.OpenRead(apkPath) )
using ( var reader = new BinaryReader(stream) )
{
if ( !stream.CanRead )
throw new ArgumentException();
if ( !stream.CanSeek )
throw new ArgumentException();
long expectedNumberOfEntries;
long centralDirectoryStart;
ZipArchiveUtils.ReadEndOfCentralDirectory(stream, reader, out expectedNumberOfEntries, out centralDirectoryStart);
try
{
stream.Seek(centralDirectoryStart, SeekOrigin.Begin);
long numberOfEntries = 0;
ZipCentralDirectoryFileHeader header;
const int prefixLength = 7;
const string prefix = "assets/";
const string assetsPrefix = "assets/bin/";
Debug.Assert(prefixLength == prefix.Length);
while ( ZipCentralDirectoryFileHeader.TryReadBlock(reader, out header) )
{
if ( header.CompressedSize != header.UncompressedSize )
{
#if UNITY_ASSERTIONS
var fileName = Encoding.UTF8.GetString(header.Filename);
if (fileName.StartsWith(prefix) && !fileName.StartsWith(assetsPrefix))
{
bool isStreamingAsset = true;
AndroidIsCompressedFileStreamingAsset(fileName, ref isStreamingAsset);
if (isStreamingAsset)
{
Debug.LogAssertionFormat("BetterStreamingAssets: file {0} is where Streaming Assets are put, but is compressed. " +
"If this is a App Bundle build, see README for a possible workaround. " +
"If this file is not a Streaming Asset (has been on purpose by hand or by another plug-in), implement " +
"BetterStreamingAssets.AndroidIsCompressedFileStreamingAsset partial method to prevent this message from appearing again. ",
fileName);
}
}
#endif
// we only want uncompressed files
}
else
{
var fileName = Encoding.UTF8.GetString(header.Filename);
if (fileName.EndsWith("/"))
{
// there's some strangeness when it comes to OBB: directories are listed as files
// simply ignoring them should be enough
Debug.Assert(header.UncompressedSize == 0);
}
else if ( fileName.StartsWith(prefix) )
{
// ignore normal assets...
if ( fileName.StartsWith(assetsPrefix) )
{
// Note: if you put bin directory in your StreamingAssets you will get false negative here
}
else
{
var relativePath = fileName.Substring(prefixLength - 1);
var entry = new PartInfo()
{
crc32 = header.Crc32,
offset = header.RelativeOffsetOfLocalHeader, // this offset will need fixing later on
size = header.UncompressedSize
};
var index = paths.BinarySearch(relativePath, StringComparer.OrdinalIgnoreCase);
if ( index >= 0 )
throw new System.InvalidOperationException("Paths duplicate! " + fileName);
paths.Insert(~index, relativePath);
parts.Insert(~index, entry);
}
}
}
numberOfEntries++;
}
if ( numberOfEntries != expectedNumberOfEntries )
throw new ZipArchiveException("Number of entries does not match");
}
catch ( EndOfStreamException ex )
{
throw new ZipArchiveException("CentralDirectoryInvalid", ex);
}
// now fix offsets
for ( int i = 0; i < parts.Count; ++i )
{
var entry = parts[i];
stream.Seek(entry.offset, SeekOrigin.Begin);
if ( !ZipLocalFileHeader.TrySkipBlock(reader) )
throw new ZipArchiveException("Local file header corrupt");
entry.offset = stream.Position;
parts[i] = entry;
}
}
}
}
#endif
}

View File

@ -1,7 +1,8 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: a4878bda4f0274c42981d0fe227e703b guid: 12bc032fc47a6c34fb90f0f4a48f4441
timeCreated: 1506516969
licenseType: Pro
MonoImporter: MonoImporter:
externalObjects: {}
serializedVersion: 2 serializedVersion: 2
defaultReferences: [] defaultReferences: []
executionOrder: 0 executionOrder: 0

View File

@ -0,0 +1,164 @@
// Better Streaming Assets, Piotr Gwiazdowski <gwiazdorrr+github at gmail.com>, 2017
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
namespace Better.StreamingAssets
{
public static partial class PathUtil
{
private enum NormalizeState
{
PrevSlash,
PrevDot,
PrevDoubleDot,
NothingSpecial,
}
public static bool IsDirectorySeparator(char c)
{
return c == '/' || c == '\\';
}
public static string FixTrailingDirectorySeparators(string path)
{
if ( path.Length >= 2 )
{
var lastChar = path[path.Length - 1];
var prevChar = path[path.Length - 2];
if ( PathUtil.IsDirectorySeparator(lastChar) && PathUtil.IsDirectorySeparator(prevChar) )
{
return path.TrimEnd('\\', '/') + lastChar;
}
}
return path;
}
public static string CombineSlash(string a, string b)
{
if ( a == null )
throw new ArgumentNullException("a");
if ( b == null )
throw new ArgumentNullException("b");
if ( string.IsNullOrEmpty(b) )
return a;
if ( string.IsNullOrEmpty(a) )
return b;
if (b[0] == '/')
return b;
if ( IsDirectorySeparator(a[a.Length -1]) )
return a + b;
else
return a + '/' + b;
}
public static string NormalizeRelativePath(string relative, bool forceTrailingSlash = false)
{
if (string.IsNullOrEmpty(relative))
throw new System.ArgumentException("Empty or null", "relative");
StringBuilder output = new StringBuilder(relative.Length);
NormalizeState state = NormalizeState.PrevSlash;
output.Append('/');
int startIndex = 0;
int lastIndexPlus1 = relative.Length;
if ( relative[0] == '"' && relative.Length > 2 && relative[relative.Length - 1] == '"')
{
startIndex++;
lastIndexPlus1--;
}
for ( int i = startIndex; i <= lastIndexPlus1; ++i )
{
if (i == lastIndexPlus1 || relative[i] == '/' || relative[i] == '\\')
{
if ( state == NormalizeState.PrevSlash || state == NormalizeState.PrevDot )
{
// do nothing
}
else if ( state == NormalizeState.PrevDoubleDot )
{
if ( output.Length == 1 )
throw new System.IO.IOException("Invalid path: double dot error (before " + i + ")");
// on level up!
int j;
for ( j = output.Length - 2; j >= 0 && output[j] != '/'; --j)
{
}
output.Remove(j + 1, output.Length - j - 1);
}
else if ( i < lastIndexPlus1 || forceTrailingSlash )
{
output.Append('/');
}
state = NormalizeState.PrevSlash;
}
else if ( relative[i] == '.' )
{
if ( state == NormalizeState.PrevSlash )
{
state = NormalizeState.PrevDot;
}
else if ( state == NormalizeState.PrevDot )
{
state = NormalizeState.PrevDoubleDot;
}
else if ( state == NormalizeState.PrevDoubleDot )
{
state = NormalizeState.NothingSpecial;
output.Append("...");
}
else
{
output.Append('.');
}
}
else
{
if ( state == NormalizeState.PrevDot )
{
output.Append('.');
}
else if ( state == NormalizeState.PrevDoubleDot )
{
output.Append("..");
}
if (!IsValidCharacter(relative[i]))
throw new System.IO.IOException("Invalid characters");
output.Append(relative[i]);
state = NormalizeState.NothingSpecial;
}
}
return output.ToString();
}
public static bool IsValidCharacter(char c)
{
if (c == '\"' || c == '<' || c == '>' || c == '|' || c < 32 || c == ':' || c == '*' || c == '?')
return false;
return true;
}
public static Regex WildcardToRegex(string pattern)
{
return new Regex("^" + Regex.Escape(pattern).Replace(@"\*", ".*").Replace(@"\?", ".") + "$", RegexOptions.IgnoreCase);
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 34f6b4de9962c114299b3b9bfdd590d5
timeCreated: 1506546248
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,168 @@
// Better Streaming Assets, Piotr Gwiazdowski <gwiazdorrr+github at gmail.com>, 2017
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
namespace Better.StreamingAssets
{
internal class SubReadOnlyStream: Stream
{
private readonly long m_offset;
private readonly bool m_leaveOpen;
private long? m_length;
private Stream m_actualStream;
private long m_position;
public SubReadOnlyStream(Stream actualStream, bool leaveOpen = false)
{
if (actualStream == null)
throw new ArgumentNullException("superStream");
m_actualStream = actualStream;
m_leaveOpen = leaveOpen;
}
public SubReadOnlyStream(Stream actualStream, long offset, long length, bool leaveOpen = false)
: this(actualStream, leaveOpen)
{
if (offset < 0)
throw new ArgumentOutOfRangeException("offset");
if (length < 0)
throw new ArgumentOutOfRangeException("length");
Debug.Assert(offset <= actualStream.Length);
Debug.Assert(actualStream.Length >= length);
Debug.Assert(offset + length <= actualStream.Length);
m_offset = offset;
m_position = offset;
m_length = length;
}
public override long Length
{
get
{
ThrowIfDisposed();
if (!m_length.HasValue)
m_length = m_actualStream.Length - m_offset;
return m_length.Value;
}
}
public override long Position
{
get
{
ThrowIfDisposed();
return m_position - m_offset;
}
set
{
ThrowIfDisposed();
m_position = m_offset + value;
}
}
public override bool CanRead { get { return m_actualStream.CanRead; } }
public override bool CanSeek { get { return m_actualStream.CanSeek; } }
public override bool CanWrite { get { return false; } }
public override int Read(byte[] buffer, int offset, int count)
{
ThrowIfCantRead();
ThrowIfDisposed();
if ( m_actualStream.Position != m_position )
m_actualStream.Seek(m_position, SeekOrigin.Begin);
if ( m_length.HasValue )
{
var endPosition = m_offset + m_length.Value;
if (m_position + count > endPosition)
{
count = (int)(endPosition - m_position);
}
}
int bytesRead = m_actualStream.Read(buffer, offset, count);
m_position += bytesRead;
return bytesRead;
}
public override long Seek(long offset, SeekOrigin origin)
{
ThrowIfDisposed();
if ( origin == SeekOrigin.Begin )
{
m_position = m_actualStream.Seek(m_offset + offset, SeekOrigin.Begin);
}
else if ( origin == SeekOrigin.End )
{
m_position = m_actualStream.Seek(m_offset + Length + offset, SeekOrigin.Begin);
}
else
{
m_position = m_actualStream.Seek(offset, SeekOrigin.Current);
}
return m_position - m_offset;
}
public override void SetLength(long value)
{
throw new NotSupportedException();
}
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotSupportedException();
}
public override void Flush()
{
throw new NotSupportedException();
}
// Close the stream for reading. Note that this does NOT close the superStream (since
// the substream is just 'a chunk' of the super-stream
protected override void Dispose(bool disposing)
{
if ( disposing )
{
if (m_actualStream != null)
{
if (!m_leaveOpen)
m_actualStream.Dispose();
m_actualStream = null;
}
}
base.Dispose(disposing);
}
private void ThrowIfDisposed()
{
if (m_actualStream == null)
throw new ObjectDisposedException(GetType().ToString(), "");
}
private void ThrowIfCantRead()
{
if (!CanRead)
throw new NotSupportedException();
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 23985058976064b4aa47f955e25b32fb
timeCreated: 1506207467
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,649 @@
// Better Streaming Assets, Piotr Gwiazdowski <gwiazdorrr+github at gmail.com>, 2017
// Bits below are copied from or inspired by System.IO.Compression.dll; leaving comments from
// original source code and attaching license
// The MIT License(MIT)
//
// Copyright(c) .NET Foundation and Contributors
//
// All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using UnityEngine;
namespace Better.StreamingAssets.ZipArchive
{
// All blocks.TryReadBlock do a check to see if signature is correct. Generic extra field is slightly different
// all of the TryReadBlocks will throw if there are not enough bytes in the stream
internal struct ZipGenericExtraField
{
private const int SizeOfHeader = 4;
private ushort _tag;
private ushort _size;
private byte[] _data;
public ushort Tag { get { return _tag; } }
// returns size of data, not of the entire block
public ushort Size { get { return _size; } }
public byte[] Data { get { return _data; } }
// shouldn't ever read the byte at position endExtraField
// assumes we are positioned at the beginning of an extra field subfield
public static bool TryReadBlock(BinaryReader reader, long endExtraField, out ZipGenericExtraField field)
{
field = new ZipGenericExtraField();
// not enough bytes to read tag + size
if ( endExtraField - reader.BaseStream.Position < 4 )
return false;
field._tag = reader.ReadUInt16();
field._size = reader.ReadUInt16();
// not enough bytes to read the data
if ( endExtraField - reader.BaseStream.Position < field._size )
return false;
field._data = reader.ReadBytes(field._size);
return true;
}
}
internal struct Zip64ExtraField
{
// Size is size of the record not including the tag or size fields
// If the extra field is going in the local header, it cannot include only
// one of uncompressed/compressed size
public const int OffsetToFirstField = 4;
private const ushort TagConstant = 1;
private ushort _size;
private long? _uncompressedSize;
private long? _compressedSize;
private long? _localHeaderOffset;
private int? _startDiskNumber;
public long? UncompressedSize
{
get { return _uncompressedSize; }
set { _uncompressedSize = value; UpdateSize(); }
}
public long? CompressedSize
{
get { return _compressedSize; }
set { _compressedSize = value; UpdateSize(); }
}
public long? LocalHeaderOffset
{
get { return _localHeaderOffset; }
set { _localHeaderOffset = value; UpdateSize(); }
}
public int? StartDiskNumber { get { return _startDiskNumber; } }
private void UpdateSize()
{
_size = 0;
if ( _uncompressedSize != null ) _size += 8;
if ( _compressedSize != null ) _size += 8;
if ( _localHeaderOffset != null ) _size += 8;
if ( _startDiskNumber != null ) _size += 4;
}
// There is a small chance that something very weird could happen here. The code calling into this function
// will ask for a value from the extra field if the field was masked with FF's. It's theoretically possible
// that a field was FF's legitimately, and the writer didn't decide to write the corresponding extra field.
// Also, at the same time, other fields were masked with FF's to indicate looking in the zip64 record.
// Then, the search for the zip64 record will fail because the expected size is wrong,
// and a nulled out Zip64ExtraField will be returned. Thus, even though there was Zip64 data,
// it will not be used. It is questionable whether this situation is possible to detect
// unlike the other functions that have try-pattern semantics, these functions always return a
// Zip64ExtraField. If a Zip64 extra field actually doesn't exist, all of the fields in the
// returned struct will be null
//
// If there are more than one Zip64 extra fields, we take the first one that has the expected size
//
public static Zip64ExtraField GetJustZip64Block(Stream extraFieldStream,
bool readUncompressedSize, bool readCompressedSize,
bool readLocalHeaderOffset, bool readStartDiskNumber)
{
Zip64ExtraField zip64Field;
using ( BinaryReader reader = new BinaryReader(extraFieldStream) )
{
ZipGenericExtraField currentExtraField;
while ( ZipGenericExtraField.TryReadBlock(reader, extraFieldStream.Length, out currentExtraField) )
{
if ( TryGetZip64BlockFromGenericExtraField(currentExtraField, readUncompressedSize,
readCompressedSize, readLocalHeaderOffset, readStartDiskNumber, out zip64Field) )
{
return zip64Field;
}
}
}
zip64Field = new Zip64ExtraField();
zip64Field._compressedSize = null;
zip64Field._uncompressedSize = null;
zip64Field._localHeaderOffset = null;
zip64Field._startDiskNumber = null;
return zip64Field;
}
private static bool TryGetZip64BlockFromGenericExtraField(ZipGenericExtraField extraField,
bool readUncompressedSize, bool readCompressedSize,
bool readLocalHeaderOffset, bool readStartDiskNumber,
out Zip64ExtraField zip64Block)
{
zip64Block = new Zip64ExtraField();
zip64Block._compressedSize = null;
zip64Block._uncompressedSize = null;
zip64Block._localHeaderOffset = null;
zip64Block._startDiskNumber = null;
if ( extraField.Tag != TagConstant )
return false;
// this pattern needed because nested using blocks trigger CA2202
MemoryStream ms = null;
try
{
ms = new MemoryStream(extraField.Data);
using ( BinaryReader reader = new BinaryReader(ms) )
{
ms = null;
zip64Block._size = extraField.Size;
ushort expectedSize = 0;
if ( readUncompressedSize ) expectedSize += 8;
if ( readCompressedSize ) expectedSize += 8;
if ( readLocalHeaderOffset ) expectedSize += 8;
if ( readStartDiskNumber ) expectedSize += 4;
// if it is not the expected size, perhaps there is another extra field that matches
if ( expectedSize != zip64Block._size )
return false;
if ( readUncompressedSize ) zip64Block._uncompressedSize = reader.ReadInt64();
if ( readCompressedSize ) zip64Block._compressedSize = reader.ReadInt64();
if ( readLocalHeaderOffset ) zip64Block._localHeaderOffset = reader.ReadInt64();
if ( readStartDiskNumber ) zip64Block._startDiskNumber = reader.ReadInt32();
// original values are unsigned, so implies value is too big to fit in signed integer
if ( zip64Block._uncompressedSize < 0 ) throw new ZipArchiveException("FieldTooBigUncompressedSize");
if ( zip64Block._compressedSize < 0 ) throw new ZipArchiveException("FieldTooBigCompressedSize");
if ( zip64Block._localHeaderOffset < 0 ) throw new ZipArchiveException("FieldTooBigLocalHeaderOffset");
if ( zip64Block._startDiskNumber < 0 ) throw new ZipArchiveException("FieldTooBigStartDiskNumber");
return true;
}
}
finally
{
if ( ms != null )
ms.Dispose();
}
}
}
internal struct Zip64EndOfCentralDirectoryLocator
{
public const uint SignatureConstant = 0x07064B50;
public const int SizeOfBlockWithoutSignature = 16;
public uint NumberOfDiskWithZip64EOCD;
public ulong OffsetOfZip64EOCD;
public uint TotalNumberOfDisks;
public static bool TryReadBlock(BinaryReader reader, out Zip64EndOfCentralDirectoryLocator zip64EOCDLocator)
{
zip64EOCDLocator = new Zip64EndOfCentralDirectoryLocator();
if ( reader.ReadUInt32() != SignatureConstant )
return false;
zip64EOCDLocator.NumberOfDiskWithZip64EOCD = reader.ReadUInt32();
zip64EOCDLocator.OffsetOfZip64EOCD = reader.ReadUInt64();
zip64EOCDLocator.TotalNumberOfDisks = reader.ReadUInt32();
return true;
}
}
internal struct Zip64EndOfCentralDirectoryRecord
{
private const uint SignatureConstant = 0x06064B50;
private const ulong NormalSize = 0x2C; // the size of the data excluding the size/signature fields if no extra data included
public ulong SizeOfThisRecord;
public ushort VersionMadeBy;
public ushort VersionNeededToExtract;
public uint NumberOfThisDisk;
public uint NumberOfDiskWithStartOfCD;
public ulong NumberOfEntriesOnThisDisk;
public ulong NumberOfEntriesTotal;
public ulong SizeOfCentralDirectory;
public ulong OffsetOfCentralDirectory;
public static bool TryReadBlock(BinaryReader reader, out Zip64EndOfCentralDirectoryRecord zip64EOCDRecord)
{
zip64EOCDRecord = new Zip64EndOfCentralDirectoryRecord();
if ( reader.ReadUInt32() != SignatureConstant )
return false;
zip64EOCDRecord.SizeOfThisRecord = reader.ReadUInt64();
zip64EOCDRecord.VersionMadeBy = reader.ReadUInt16();
zip64EOCDRecord.VersionNeededToExtract = reader.ReadUInt16();
zip64EOCDRecord.NumberOfThisDisk = reader.ReadUInt32();
zip64EOCDRecord.NumberOfDiskWithStartOfCD = reader.ReadUInt32();
zip64EOCDRecord.NumberOfEntriesOnThisDisk = reader.ReadUInt64();
zip64EOCDRecord.NumberOfEntriesTotal = reader.ReadUInt64();
zip64EOCDRecord.SizeOfCentralDirectory = reader.ReadUInt64();
zip64EOCDRecord.OffsetOfCentralDirectory = reader.ReadUInt64();
return true;
}
}
internal struct ZipLocalFileHeader
{
public const uint DataDescriptorSignature = 0x08074B50;
public const uint SignatureConstant = 0x04034B50;
public const int OffsetToCrcFromHeaderStart = 14;
public const int OffsetToBitFlagFromHeaderStart = 6;
public const int SizeOfLocalHeader = 30;
// will not throw end of stream exception
public static bool TrySkipBlock(BinaryReader reader)
{
const int OffsetToFilenameLength = 22; // from the point after the signature
if ( reader.ReadUInt32() != SignatureConstant )
return false;
if ( reader.BaseStream.Length < reader.BaseStream.Position + OffsetToFilenameLength )
return false;
reader.BaseStream.Seek(OffsetToFilenameLength, SeekOrigin.Current);
ushort filenameLength = reader.ReadUInt16();
ushort extraFieldLength = reader.ReadUInt16();
if ( reader.BaseStream.Length < reader.BaseStream.Position + filenameLength + extraFieldLength )
return false;
reader.BaseStream.Seek(filenameLength + extraFieldLength, SeekOrigin.Current);
return true;
}
}
internal struct ZipCentralDirectoryFileHeader
{
public const uint SignatureConstant = 0x02014B50;
public byte VersionMadeByCompatibility;
public byte VersionMadeBySpecification;
public ushort VersionNeededToExtract;
public ushort GeneralPurposeBitFlag;
public ushort CompressionMethod;
public uint LastModified; // convert this on the fly
public uint Crc32;
public long CompressedSize;
public long UncompressedSize;
public ushort FilenameLength;
public ushort ExtraFieldLength;
public ushort FileCommentLength;
public int DiskNumberStart;
public ushort InternalFileAttributes;
public uint ExternalFileAttributes;
public long RelativeOffsetOfLocalHeader;
public byte[] Filename;
public byte[] FileComment;
public List<ZipGenericExtraField> ExtraFields;
// if saveExtraFieldsAndComments is false, FileComment and ExtraFields will be null
// in either case, the zip64 extra field info will be incorporated into other fields
public static bool TryReadBlock(BinaryReader reader, out ZipCentralDirectoryFileHeader header)
{
header = new ZipCentralDirectoryFileHeader();
if ( reader.ReadUInt32() != SignatureConstant )
return false;
header.VersionMadeBySpecification = reader.ReadByte();
header.VersionMadeByCompatibility = reader.ReadByte();
header.VersionNeededToExtract = reader.ReadUInt16();
header.GeneralPurposeBitFlag = reader.ReadUInt16();
header.CompressionMethod = reader.ReadUInt16();
header.LastModified = reader.ReadUInt32();
header.Crc32 = reader.ReadUInt32();
uint compressedSizeSmall = reader.ReadUInt32();
uint uncompressedSizeSmall = reader.ReadUInt32();
header.FilenameLength = reader.ReadUInt16();
header.ExtraFieldLength = reader.ReadUInt16();
header.FileCommentLength = reader.ReadUInt16();
ushort diskNumberStartSmall = reader.ReadUInt16();
header.InternalFileAttributes = reader.ReadUInt16();
header.ExternalFileAttributes = reader.ReadUInt32();
uint relativeOffsetOfLocalHeaderSmall = reader.ReadUInt32();
header.Filename = reader.ReadBytes(header.FilenameLength);
bool uncompressedSizeInZip64 = uncompressedSizeSmall == ZipHelper.Mask32Bit;
bool compressedSizeInZip64 = compressedSizeSmall == ZipHelper.Mask32Bit;
bool relativeOffsetInZip64 = relativeOffsetOfLocalHeaderSmall == ZipHelper.Mask32Bit;
bool diskNumberStartInZip64 = diskNumberStartSmall == ZipHelper.Mask16Bit;
Zip64ExtraField zip64;
long endExtraFields = reader.BaseStream.Position + header.ExtraFieldLength;
using ( Stream str = new SubReadOnlyStream(reader.BaseStream, reader.BaseStream.Position, header.ExtraFieldLength, leaveOpen: true) )
{
header.ExtraFields = null;
zip64 = Zip64ExtraField.GetJustZip64Block(str,
uncompressedSizeInZip64, compressedSizeInZip64,
relativeOffsetInZip64, diskNumberStartInZip64);
}
// There are zip files that have malformed ExtraField blocks in which GetJustZip64Block() silently bails out without reading all the way to the end
// of the ExtraField block. Thus we must force the stream's position to the proper place.
reader.BaseStream.AdvanceToPosition(endExtraFields);
reader.BaseStream.Position += header.FileCommentLength;
header.FileComment = null;
header.UncompressedSize = zip64.UncompressedSize == null
? uncompressedSizeSmall
: zip64.UncompressedSize.Value;
header.CompressedSize = zip64.CompressedSize == null
? compressedSizeSmall
: zip64.CompressedSize.Value;
header.RelativeOffsetOfLocalHeader = zip64.LocalHeaderOffset == null
? relativeOffsetOfLocalHeaderSmall
: zip64.LocalHeaderOffset.Value;
header.DiskNumberStart = zip64.StartDiskNumber == null
? diskNumberStartSmall
: zip64.StartDiskNumber.Value;
return true;
}
}
internal struct ZipEndOfCentralDirectoryBlock
{
public const uint SignatureConstant = 0x06054B50;
public const int SizeOfBlockWithoutSignature = 18;
public uint Signature;
public ushort NumberOfThisDisk;
public ushort NumberOfTheDiskWithTheStartOfTheCentralDirectory;
public ushort NumberOfEntriesInTheCentralDirectoryOnThisDisk;
public ushort NumberOfEntriesInTheCentralDirectory;
public uint SizeOfCentralDirectory;
public uint OffsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber;
public byte[] ArchiveComment;
public static bool TryReadBlock(BinaryReader reader, out ZipEndOfCentralDirectoryBlock eocdBlock)
{
eocdBlock = new ZipEndOfCentralDirectoryBlock();
if ( reader.ReadUInt32() != SignatureConstant )
return false;
eocdBlock.Signature = SignatureConstant;
eocdBlock.NumberOfThisDisk = reader.ReadUInt16();
eocdBlock.NumberOfTheDiskWithTheStartOfTheCentralDirectory = reader.ReadUInt16();
eocdBlock.NumberOfEntriesInTheCentralDirectoryOnThisDisk = reader.ReadUInt16();
eocdBlock.NumberOfEntriesInTheCentralDirectory = reader.ReadUInt16();
eocdBlock.SizeOfCentralDirectory = reader.ReadUInt32();
eocdBlock.OffsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber = reader.ReadUInt32();
ushort commentLength = reader.ReadUInt16();
eocdBlock.ArchiveComment = reader.ReadBytes(commentLength);
return true;
}
}
internal static class ZipHelper
{
internal const uint Mask32Bit = 0xFFFFFFFF;
internal const ushort Mask16Bit = 0xFFFF;
private const int BackwardsSeekingBufferSize = 32;
/// <summary>
/// Reads exactly bytesToRead out of stream, unless it is out of bytes
/// </summary>
internal static void ReadBytes(Stream stream, byte[] buffer, int bytesToRead)
{
int bytesLeftToRead = bytesToRead;
int totalBytesRead = 0;
while (bytesLeftToRead > 0)
{
int bytesRead = stream.Read(buffer, totalBytesRead, bytesLeftToRead);
if (bytesRead == 0) throw new IOException();
totalBytesRead += bytesRead;
bytesLeftToRead -= bytesRead;
}
}
// assumes all bytes of signatureToFind are non zero, looks backwards from current position in stream,
// if the signature is found then returns true and positions stream at first byte of signature
// if the signature is not found, returns false
internal static bool SeekBackwardsToSignature(Stream stream, uint signatureToFind)
{
int bufferPointer = 0;
uint currentSignature = 0;
byte[] buffer = new byte[BackwardsSeekingBufferSize];
bool outOfBytes = false;
bool signatureFound = false;
while (!signatureFound && !outOfBytes)
{
outOfBytes = SeekBackwardsAndRead(stream, buffer, out bufferPointer);
Debug.Assert(bufferPointer < buffer.Length);
while (bufferPointer >= 0 && !signatureFound)
{
currentSignature = (currentSignature << 8) | ((uint)buffer[bufferPointer]);
if (currentSignature == signatureToFind)
{
signatureFound = true;
}
else
{
bufferPointer--;
}
}
}
if (!signatureFound)
{
return false;
}
else
{
stream.Seek(bufferPointer, SeekOrigin.Current);
return true;
}
}
// Skip to a further position downstream (without relying on the stream being seekable)
internal static void AdvanceToPosition(this Stream stream, long position)
{
long numBytesLeft = position - stream.Position;
Debug.Assert(numBytesLeft >= 0);
while (numBytesLeft != 0)
{
const int throwAwayBufferSize = 64;
int numBytesToSkip = (numBytesLeft > throwAwayBufferSize) ? throwAwayBufferSize : (int)numBytesLeft;
int numBytesActuallySkipped = stream.Read(new byte[throwAwayBufferSize], 0, numBytesToSkip);
if (numBytesActuallySkipped == 0)
throw new IOException();
numBytesLeft -= numBytesActuallySkipped;
}
}
// Returns true if we are out of bytes
private static bool SeekBackwardsAndRead(Stream stream, byte[] buffer, out int bufferPointer)
{
if (stream.Position >= buffer.Length)
{
stream.Seek(-buffer.Length, SeekOrigin.Current);
ReadBytes(stream, buffer, buffer.Length);
stream.Seek(-buffer.Length, SeekOrigin.Current);
bufferPointer = buffer.Length - 1;
return false;
}
else
{
int bytesToRead = (int)stream.Position;
stream.Seek(0, SeekOrigin.Begin);
ReadBytes(stream, buffer, bytesToRead);
stream.Seek(0, SeekOrigin.Begin);
bufferPointer = bytesToRead - 1;
return true;
}
}
}
public class ZipArchiveException : Exception
{
public ZipArchiveException(string msg) : base(msg)
{ }
public ZipArchiveException(string msg, Exception inner)
: base(msg, inner)
{
}
}
public static class ZipArchiveUtils
{
public static void ReadEndOfCentralDirectory(Stream stream, BinaryReader reader, out long expectedNumberOfEntries, out long centralDirectoryStart)
{
try
{
// this seeks to the start of the end of central directory record
stream.Seek(-ZipEndOfCentralDirectoryBlock.SizeOfBlockWithoutSignature, SeekOrigin.End);
if (!ZipHelper.SeekBackwardsToSignature(stream, ZipEndOfCentralDirectoryBlock.SignatureConstant))
throw new ZipArchiveException("SignatureConstant");
long eocdStart = stream.Position;
// read the EOCD
ZipEndOfCentralDirectoryBlock eocd;
bool eocdProper = ZipEndOfCentralDirectoryBlock.TryReadBlock(reader, out eocd);
Debug.Assert(eocdProper); // we just found this using the signature finder, so it should be okay
if (eocd.NumberOfThisDisk != eocd.NumberOfTheDiskWithTheStartOfTheCentralDirectory)
throw new ZipArchiveException("SplitSpanned");
centralDirectoryStart = eocd.OffsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber;
if (eocd.NumberOfEntriesInTheCentralDirectory != eocd.NumberOfEntriesInTheCentralDirectoryOnThisDisk)
throw new ZipArchiveException("SplitSpanned");
expectedNumberOfEntries = eocd.NumberOfEntriesInTheCentralDirectory;
// only bother looking for zip64 EOCD stuff if we suspect it is needed because some value is FFFFFFFFF
// because these are the only two values we need, we only worry about these
// if we don't find the zip64 EOCD, we just give up and try to use the original values
if (eocd.NumberOfThisDisk == ZipHelper.Mask16Bit ||
eocd.OffsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber == ZipHelper.Mask32Bit ||
eocd.NumberOfEntriesInTheCentralDirectory == ZipHelper.Mask16Bit)
{
// we need to look for zip 64 EOCD stuff
// seek to the zip 64 EOCD locator
stream.Seek(eocdStart - Zip64EndOfCentralDirectoryLocator.SizeOfBlockWithoutSignature, SeekOrigin.Begin);
// if we don't find it, assume it doesn't exist and use data from normal eocd
if (ZipHelper.SeekBackwardsToSignature(stream, Zip64EndOfCentralDirectoryLocator.SignatureConstant))
{
// use locator to get to Zip64EOCD
Zip64EndOfCentralDirectoryLocator locator;
bool zip64eocdLocatorProper = Zip64EndOfCentralDirectoryLocator.TryReadBlock(reader, out locator);
Debug.Assert(zip64eocdLocatorProper); // we just found this using the signature finder, so it should be okay
if (locator.OffsetOfZip64EOCD > long.MaxValue)
throw new ZipArchiveException("FieldTooBigOffsetToZip64EOCD");
long zip64EOCDOffset = (long)locator.OffsetOfZip64EOCD;
stream.Seek(zip64EOCDOffset, SeekOrigin.Begin);
// read Zip64EOCD
Zip64EndOfCentralDirectoryRecord record;
if (!Zip64EndOfCentralDirectoryRecord.TryReadBlock(reader, out record))
throw new ZipArchiveException("Zip64EOCDNotWhereExpected");
if (record.NumberOfEntriesTotal > long.MaxValue)
throw new ZipArchiveException("FieldTooBigNumEntries");
if (record.OffsetOfCentralDirectory > long.MaxValue)
throw new ZipArchiveException("FieldTooBigOffsetToCD");
if (record.NumberOfEntriesTotal != record.NumberOfEntriesOnThisDisk)
throw new ZipArchiveException("SplitSpanned");
expectedNumberOfEntries = (long)record.NumberOfEntriesTotal;
centralDirectoryStart = (long)record.OffsetOfCentralDirectory;
}
}
if (centralDirectoryStart > stream.Length)
{
throw new ZipArchiveException("FieldTooBigOffsetToCD");
}
}
catch (EndOfStreamException ex)
{
throw new ZipArchiveException("CDCorrupt", ex);
}
catch (IOException ex)
{
throw new ZipArchiveException("CDCorrupt", ex);
}
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 4ae0668083250b649a64c7836ec20f11
timeCreated: 1506207467
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
{
"name": "BetterStreamingAssets",
"references": [],
"optionalUnityReferences": [],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false
}

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: cf4f1d4730c114c48ab680458e5a2455 guid: 5e90fee04fdf7164fa71eeef34c6a431
AssemblyDefinitionImporter: AssemblyDefinitionImporter:
externalObjects: {} externalObjects: {}
userData: userData:

View File

@ -0,0 +1,16 @@
{
"name": "com.gwiazdorrr.betterstreamingassets",
"displayName": "Better Streaming Assets",
"author": { "name": "Piotr Gwiazdowski" },
"version": "1.6.0",
"documentationUrl": "https://github.com/gwiazdorrr/BetterStreamingAssets",
"changelogUrl": "https://github.com/gwiazdorrr/BetterStreamingAssets",
"licensesUrl": "https://github.com/gwiazdorrr/BetterStreamingAssets/blob/master/LICENSE",
"description": "Access Streaming Assets directly in an uniform and thread-safe way, with tiny overhead.",
"samples": [
{
"displayName": "Better Streaming Assets Samples",
"path": "Samples~/BetterStreamingAssets"
}
]
}

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 08dafaa194a7143109cbe1c6403a2ec5
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -37,20 +37,15 @@ namespace YooAsset.Editor
if (string.IsNullOrEmpty(_patchManifestPath) == false) if (string.IsNullOrEmpty(_patchManifestPath) == false)
{ {
if (GUILayout.Button("导入补丁包(内置文件)", GUILayout.MaxWidth(150)))
{
AssetBundleBuilderHelper.ClearStreamingAssetsFolder();
CopyPatchFiles(_patchManifestPath, false);
}
if (GUILayout.Button("导入补丁包(全部文件)", GUILayout.MaxWidth(150))) if (GUILayout.Button("导入补丁包(全部文件)", GUILayout.MaxWidth(150)))
{ {
AssetBundleBuilderHelper.ClearStreamingAssetsFolder(); AssetBundleBuilderHelper.ClearStreamingAssetsFolder();
CopyPatchFiles(_patchManifestPath, true); CopyPatchFiles(_patchManifestPath);
} }
} }
} }
private void CopyPatchFiles(string patchManifestFilePath, bool allPatchFile) private void CopyPatchFiles(string patchManifestFilePath)
{ {
string manifestFileName = Path.GetFileNameWithoutExtension(patchManifestFilePath); string manifestFileName = Path.GetFileNameWithoutExtension(patchManifestFilePath);
string outputDirectory = Path.GetDirectoryName(patchManifestFilePath); string outputDirectory = Path.GetDirectoryName(patchManifestFilePath);
@ -74,9 +69,6 @@ namespace YooAsset.Editor
// 拷贝文件列表 // 拷贝文件列表
int fileCount = 0; int fileCount = 0;
if (allPatchFile)
{
foreach (var patchBundle in patchManifest.BundleList) foreach (var patchBundle in patchManifest.BundleList)
{ {
fileCount++; fileCount++;
@ -84,20 +76,6 @@ namespace YooAsset.Editor
string destPath = $"{AssetBundleBuilderHelper.GetStreamingAssetsFolderPath()}/{patchBundle.FileName}"; string destPath = $"{AssetBundleBuilderHelper.GetStreamingAssetsFolderPath()}/{patchBundle.FileName}";
EditorTools.CopyFile(sourcePath, destPath, true); EditorTools.CopyFile(sourcePath, destPath, true);
} }
}
else
{
foreach (var patchBundle in patchManifest.BundleList)
{
if (patchBundle.IsBuildin == false)
continue;
fileCount++;
string sourcePath = $"{outputDirectory}/{patchBundle.FileName}";
string destPath = $"{AssetBundleBuilderHelper.GetStreamingAssetsFolderPath()}/{patchBundle.FileName}";
EditorTools.CopyFile(sourcePath, destPath, true);
}
}
Debug.Log($"补丁包拷贝完成,一共拷贝了{fileCount}个资源文件"); Debug.Log($"补丁包拷贝完成,一共拷贝了{fileCount}个资源文件");
AssetDatabase.Refresh(); AssetDatabase.Refresh();

View File

@ -1,221 +0,0 @@
using System;
using System.Collections;
using System.Threading.Tasks;
using UnityEngine;
using YooAsset;
public class TestScene : MonoBehaviour
{
public YooAssets.EPlayMode PlayMode = YooAssets.EPlayMode.EditorSimulateMode;
void Awake()
{
Application.targetFrameRate = 60;
Application.runInBackground = true;
}
void OnGUI()
{
GUIConsole.OnGUI();
}
IEnumerator Start()
{
Debug.Log($"资源系统运行模式:{PlayMode}");
// 编辑器模拟模式
if (PlayMode == YooAssets.EPlayMode.EditorSimulateMode)
{
var createParameters = new YooAssets.EditorSimulateModeParameters();
createParameters.LocationServices = new DefaultLocationServices("Assets/GameRes");
yield return YooAssets.InitializeAsync(createParameters);
}
// 单机模式
if (PlayMode == YooAssets.EPlayMode.OfflinePlayMode)
{
var createParameters = new YooAssets.OfflinePlayModeParameters();
createParameters.LocationServices = new DefaultLocationServices("Assets/GameRes");
yield return YooAssets.InitializeAsync(createParameters);
}
// 联机模式
if (PlayMode == YooAssets.EPlayMode.HostPlayMode)
{
throw new NotImplementedException();
}
// 开始测试
BeginTest();
}
void BeginTest()
{
AutoTestLog("开启单元测试 !");
// 开始同步测试
SyncTest();
}
void OverTest()
{
AutoTestLog("结束单元测试 !");
}
void AutoTestLog(string info)
{
Debug.Log($"[{Time.frameCount}] {info}");
}
#region 同步测试
void SyncTest()
{
SyncTest1();
SyncTest2();
// 开始回调测试
CallbackTest();
}
void SyncTest1()
{
AutoTestLog($"开始同步加载游戏对象测试 !");
var handle = YooAssets.LoadAssetSync<GameObject>("Entity/Cube/cube1");
Debug.Assert(handle.Status == EOperationStatus.Succeed);
var go = handle.InstantiateSync();
Debug.Assert(go != null);
GameObject.Destroy(go);
handle.Release();
}
void SyncTest2()
{
AutoTestLog($"开始同步加载TexturePacker图集测试 !");
var handle = YooAssets.LoadSubAssetsSync<Sprite>("UIAtlas/TexturePacker/tpAtlas");
Debug.Assert(handle.Status == EOperationStatus.Succeed);
var sprite = handle.GetSubAssetObject<Sprite>("Icon_Sword_128");
Debug.Assert(sprite != null);
handle.Release();
}
#endregion
#region 回调测试
void CallbackTest()
{
CallbackTest1();
}
void CallbackTest1()
{
AutoTestLog($"开始异步加载游戏对象,回调测试 !");
var handle = YooAssets.LoadAssetAsync<GameObject>("Entity/Cube/cube2");
handle.Completed += (h) =>
{
Debug.Assert(handle.Status == EOperationStatus.Succeed);
var operation = handle.InstantiateAsync();
operation.Completed += (o) =>
{
Debug.Assert(operation.Status == EOperationStatus.Succeed);
Debug.Assert(operation.Result != null);
GameObject.Destroy(operation.Result);
handle.Release();
CallbackTest2();
};
};
}
void CallbackTest2()
{
AutoTestLog($"开始异步加载原生文件,回调测试 !");
var operation = YooAssets.GetRawFileAsync("Config/config2");
operation.Completed += (o) =>
{
Debug.Assert(operation.Status == EOperationStatus.Succeed);
// 开始协程测试
this.StartCoroutine(CoroutineTest());
};
}
#endregion
#region 协程测试
IEnumerator CoroutineTest()
{
yield return CoroutineTest1();
yield return CoroutineTest2();
//开始Task测试
TaskTest();
}
IEnumerator CoroutineTest1()
{
AutoTestLog($"开始异步加载游戏对象,协程测试 !");
var handle = YooAssets.LoadAssetAsync<GameObject>("Entity/Cube/cube3");
yield return handle;
Debug.Assert(handle.Status == EOperationStatus.Succeed);
var operation = handle.InstantiateAsync();
yield return operation;
Debug.Assert(operation.Status == EOperationStatus.Succeed);
Debug.Assert(operation.Result != null);
GameObject.Destroy(operation.Result);
handle.Release();
}
IEnumerator CoroutineTest2()
{
AutoTestLog($"开始异步加载原生文件,协程测试 !");
var operation = YooAssets.GetRawFileAsync("Config/config3");
yield return operation;
Debug.Assert(operation.Status == EOperationStatus.Succeed);
yield return operation;
Debug.Assert(operation.Status == EOperationStatus.Succeed);
}
#endregion
#region Task测试
async void TaskTest()
{
await TaskTest1();
await TaskTest2();
// 开始错误测试
ErrorTest();
}
async Task TaskTest1()
{
AutoTestLog($"开始异步加载游戏对象Task测试 !");
var handle = YooAssets.LoadAssetAsync<GameObject>("Entity/Cube/cube4");
await handle.Task;
Debug.Assert(handle.Status == EOperationStatus.Succeed);
var operation = handle.InstantiateAsync();
await operation.Task;
Debug.Assert(operation.Status == EOperationStatus.Succeed);
Debug.Assert(operation.Result != null);
GameObject.Destroy(operation.Result);
handle.Release();
}
async Task TaskTest2()
{
AutoTestLog($"开始异步加载原生文件Task测试 !");
var operation = YooAssets.GetRawFileAsync("Config/config4");
await operation.Task;
Debug.Assert(operation.Status == EOperationStatus.Succeed);
await operation.Task;
Debug.Assert(operation.Status == EOperationStatus.Succeed);
}
#endregion
#region 错误测试
void ErrorTest()
{
AutoTestLog($"开始错误加载的测试 !");
var handle1 = YooAssets.LoadAssetSync<GameObject>("");
Debug.Assert(handle1.Status == EOperationStatus.Failed);
var handle2 = YooAssets.LoadAssetSync<GameObject>("xxx1");
Debug.Assert(handle2.Status == EOperationStatus.Failed);
var result = YooAssets.IsNeedDownloadFromRemote("xxx2");
Debug.Assert(result == false);
var operaiton = YooAssets.GetRawFileAsync("xxx3");
Debug.Assert(operaiton.Status == EOperationStatus.Failed);
// 结束测试
OverTest();
}
#endregion
}

View File

@ -1,492 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!29 &1
OcclusionCullingSettings:
m_ObjectHideFlags: 0
serializedVersion: 2
m_OcclusionBakeSettings:
smallestOccluder: 5
smallestHole: 0.25
backfaceThreshold: 100
m_SceneGUID: 00000000000000000000000000000000
m_OcclusionCullingData: {fileID: 0}
--- !u!104 &2
RenderSettings:
m_ObjectHideFlags: 0
serializedVersion: 9
m_Fog: 0
m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1}
m_FogMode: 3
m_FogDensity: 0.01
m_LinearFogStart: 0
m_LinearFogEnd: 300
m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1}
m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1}
m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1}
m_AmbientIntensity: 1
m_AmbientMode: 0
m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1}
m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0}
m_HaloStrength: 0.5
m_FlareStrength: 1
m_FlareFadeSpeed: 3
m_HaloTexture: {fileID: 0}
m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0}
m_DefaultReflectionMode: 0
m_DefaultReflectionResolution: 128
m_ReflectionBounces: 1
m_ReflectionIntensity: 1
m_CustomReflection: {fileID: 0}
m_Sun: {fileID: 0}
m_IndirectSpecularColor: {r: 0.37311953, g: 0.38074014, b: 0.3587274, a: 1}
m_UseRadianceAmbientProbe: 0
--- !u!157 &3
LightmapSettings:
m_ObjectHideFlags: 0
serializedVersion: 12
m_GIWorkflowMode: 1
m_GISettings:
serializedVersion: 2
m_BounceScale: 1
m_IndirectOutputScale: 1
m_AlbedoBoost: 1
m_EnvironmentLightingMode: 0
m_EnableBakedLightmaps: 1
m_EnableRealtimeLightmaps: 0
m_LightmapEditorSettings:
serializedVersion: 12
m_Resolution: 2
m_BakeResolution: 40
m_AtlasSize: 1024
m_AO: 0
m_AOMaxDistance: 1
m_CompAOExponent: 1
m_CompAOExponentDirect: 0
m_ExtractAmbientOcclusion: 0
m_Padding: 2
m_LightmapParameters: {fileID: 0}
m_LightmapsBakeMode: 1
m_TextureCompression: 1
m_FinalGather: 0
m_FinalGatherFiltering: 1
m_FinalGatherRayCount: 256
m_ReflectionCompression: 2
m_MixedBakeMode: 2
m_BakeBackend: 1
m_PVRSampling: 1
m_PVRDirectSampleCount: 32
m_PVRSampleCount: 500
m_PVRBounces: 2
m_PVREnvironmentSampleCount: 500
m_PVREnvironmentReferencePointCount: 2048
m_PVRFilteringMode: 2
m_PVRDenoiserTypeDirect: 0
m_PVRDenoiserTypeIndirect: 0
m_PVRDenoiserTypeAO: 0
m_PVRFilterTypeDirect: 0
m_PVRFilterTypeIndirect: 0
m_PVRFilterTypeAO: 0
m_PVREnvironmentMIS: 0
m_PVRCulling: 1
m_PVRFilteringGaussRadiusDirect: 1
m_PVRFilteringGaussRadiusIndirect: 5
m_PVRFilteringGaussRadiusAO: 2
m_PVRFilteringAtrousPositionSigmaDirect: 0.5
m_PVRFilteringAtrousPositionSigmaIndirect: 2
m_PVRFilteringAtrousPositionSigmaAO: 1
m_ExportTrainingData: 0
m_TrainingDataDestination: TrainingData
m_LightProbeSampleCountMultiplier: 4
m_LightingDataAsset: {fileID: 0}
m_LightingSettings: {fileID: 223974226}
--- !u!196 &4
NavMeshSettings:
serializedVersion: 2
m_ObjectHideFlags: 0
m_BuildSettings:
serializedVersion: 2
agentTypeID: 0
agentRadius: 0.5
agentHeight: 2
agentSlope: 45
agentClimb: 0.4
ledgeDropHeight: 0
maxJumpAcrossDistance: 0
minRegionArea: 2
manualCellSize: 0
cellSize: 0.16666667
manualTileSize: 0
tileSize: 256
accuratePlacement: 0
maxJobWorkers: 0
preserveTilesOutsideBounds: 0
debug:
m_Flags: 0
m_NavMeshData: {fileID: 0}
--- !u!850595691 &223974226
LightingSettings:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: Settings.lighting
serializedVersion: 3
m_GIWorkflowMode: 1
m_EnableBakedLightmaps: 1
m_EnableRealtimeLightmaps: 0
m_RealtimeEnvironmentLighting: 1
m_BounceScale: 1
m_AlbedoBoost: 1
m_IndirectOutputScale: 1
m_UsingShadowmask: 1
m_BakeBackend: 1
m_LightmapMaxSize: 1024
m_BakeResolution: 40
m_Padding: 2
m_TextureCompression: 1
m_AO: 0
m_AOMaxDistance: 1
m_CompAOExponent: 1
m_CompAOExponentDirect: 0
m_ExtractAO: 0
m_MixedBakeMode: 2
m_LightmapsBakeMode: 1
m_FilterMode: 1
m_LightmapParameters: {fileID: 15204, guid: 0000000000000000f000000000000000, type: 0}
m_ExportTrainingData: 0
m_TrainingDataDestination: TrainingData
m_RealtimeResolution: 2
m_ForceWhiteAlbedo: 0
m_ForceUpdates: 0
m_FinalGather: 0
m_FinalGatherRayCount: 256
m_FinalGatherFiltering: 1
m_PVRCulling: 1
m_PVRSampling: 1
m_PVRDirectSampleCount: 32
m_PVRSampleCount: 500
m_PVREnvironmentSampleCount: 500
m_PVREnvironmentReferencePointCount: 2048
m_LightProbeSampleCountMultiplier: 4
m_PVRBounces: 2
m_PVRMinBounces: 2
m_PVREnvironmentMIS: 0
m_PVRFilteringMode: 2
m_PVRDenoiserTypeDirect: 0
m_PVRDenoiserTypeIndirect: 0
m_PVRDenoiserTypeAO: 0
m_PVRFilterTypeDirect: 0
m_PVRFilterTypeIndirect: 0
m_PVRFilterTypeAO: 0
m_PVRFilteringGaussRadiusDirect: 1
m_PVRFilteringGaussRadiusIndirect: 5
m_PVRFilteringGaussRadiusAO: 2
m_PVRFilteringAtrousPositionSigmaDirect: 0.5
m_PVRFilteringAtrousPositionSigmaIndirect: 2
m_PVRFilteringAtrousPositionSigmaAO: 1
--- !u!1 &882660809
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 882660812}
- component: {fileID: 882660811}
- component: {fileID: 882660810}
m_Layer: 0
m_Name: Camera
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!81 &882660810
AudioListener:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 882660809}
m_Enabled: 1
--- !u!20 &882660811
Camera:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 882660809}
m_Enabled: 1
serializedVersion: 2
m_ClearFlags: 2
m_BackGroundColor: {r: 0, g: 0, b: 0, a: 0}
m_projectionMatrixMode: 1
m_GateFitMode: 2
m_FOVAxisMode: 0
m_SensorSize: {x: 36, y: 24}
m_LensShift: {x: 0, y: 0}
m_FocalLength: 50
m_NormalizedViewPortRect:
serializedVersion: 2
x: 0
y: 0
width: 1
height: 1
near clip plane: 0.3
far clip plane: 1000
field of view: 60
orthographic: 0
orthographic size: 5
m_Depth: 0
m_CullingMask:
serializedVersion: 2
m_Bits: 4294967295
m_RenderingPath: -1
m_TargetTexture: {fileID: 0}
m_TargetDisplay: 0
m_TargetEye: 3
m_HDR: 1
m_AllowMSAA: 1
m_AllowDynamicResolution: 0
m_ForceIntoRT: 0
m_OcclusionCulling: 1
m_StereoConvergence: 10
m_StereoSeparation: 0.022
--- !u!4 &882660812
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 882660809}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: -0.09431402, y: 0.086022705, z: -11.001953}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &1281760859
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1281760861}
- component: {fileID: 1281760860}
m_Layer: 0
m_Name: Test
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!114 &1281760860
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1281760859}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: a4878bda4f0274c42981d0fe227e703b, type: 3}
m_Name:
m_EditorClassIdentifier:
PlayMode: 0
--- !u!4 &1281760861
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1281760859}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 1
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &6616392776483865520
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 6616392776483865523}
- component: {fileID: 6616392776483865525}
- component: {fileID: 6616392776483865522}
m_Layer: 5
m_Name: title
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!114 &6616392776483865522
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6616392776483865520}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_FontData:
m_Font: {fileID: 12800000, guid: 715939d12ae22b649b9f920fdc981c7c, type: 3}
m_FontSize: 42
m_FontStyle: 1
m_BestFit: 0
m_MinSize: 3
m_MaxSize: 42
m_Alignment: 4
m_AlignByGeometry: 0
m_RichText: 1
m_HorizontalOverflow: 0
m_VerticalOverflow: 0
m_LineSpacing: 1
m_Text: "\u5355\u5143\u6D4B\u8BD5"
--- !u!224 &6616392776483865523
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6616392776483865520}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 6616392776828290750}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0.5}
m_AnchorMax: {x: 1, y: 0.5}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 100}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &6616392776483865525
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6616392776483865520}
m_CullTransparentMesh: 1
--- !u!1 &6616392776828290746
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 6616392776828290750}
- component: {fileID: 6616392776828290751}
- component: {fileID: 6616392776828290748}
- component: {fileID: 6616392776828290749}
m_Layer: 5
m_Name: Canvas
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!114 &6616392776828290748
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6616392776828290746}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3}
m_Name:
m_EditorClassIdentifier:
m_UiScaleMode: 1
m_ReferencePixelsPerUnit: 100
m_ScaleFactor: 1
m_ReferenceResolution: {x: 1280, y: 720}
m_ScreenMatchMode: 2
m_MatchWidthOrHeight: 0
m_PhysicalUnit: 3
m_FallbackScreenDPI: 96
m_DefaultSpriteDPI: 96
m_DynamicPixelsPerUnit: 1
m_PresetInfoIsWorld: 0
--- !u!114 &6616392776828290749
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6616392776828290746}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3}
m_Name:
m_EditorClassIdentifier:
m_IgnoreReversedGraphics: 1
m_BlockingObjects: 0
m_BlockingMask:
serializedVersion: 2
m_Bits: 4294967295
--- !u!224 &6616392776828290750
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6616392776828290746}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 0, y: 0, z: 0}
m_Children:
- {fileID: 6616392776483865523}
m_Father: {fileID: 0}
m_RootOrder: 2
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0, y: 0}
--- !u!223 &6616392776828290751
Canvas:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6616392776828290746}
m_Enabled: 1
serializedVersion: 3
m_RenderMode: 1
m_Camera: {fileID: 0}
m_PlaneDistance: 100
m_PixelPerfect: 0
m_ReceivesEvents: 1
m_OverrideSorting: 0
m_OverridePixelPerfect: 0
m_SortingBucketNormalizedSize: 0
m_AdditionalShaderChannelsFlag: 0
m_SortingLayerID: 0
m_SortingOrder: 0
m_TargetDisplay: 0