feat : support encrypted ArchiveBundle

This commit is contained in:
何冠峰
2026-05-28 12:02:59 +08:00
parent d5a9b9f0f4
commit 289dee326b
118 changed files with 1139 additions and 311 deletions

View File

@@ -27,14 +27,6 @@ namespace YooAsset.Editor
/// <inheritdoc />
protected override void CheckBuildParametersCore()
{
// ArchiveBundle 不支持资源包加密
if (BundleEncryptor != null)
{
string message = BuildLogger.GetErrorMessage(ErrorCode.BundleEncryptionNotSupported,
$"ArchiveFileBuildPipeline does not support bundle encryption. Please remove the BundleEncryptor configuration.");
throw new NotSupportedException(message);
}
// 校验文件对齐参数范围
if (FileAlignment < 0 || FileAlignment > MaxFileAlignment)
{

View File

@@ -38,26 +38,51 @@ namespace YooAsset
if (_steps == ESteps.LoadBundle)
{
if (_options.Bundle.IsEncrypted)
if (_options.Bundle.IsEncrypted == false)
{
_steps = ESteps.Done;
SetError($"ArchiveBundle encrypted loading is not supported: '{_options.FilePath}'.");
return;
}
if (FileUtility.IsFileIOSupported(_options.FilePath) == false)
{
_steps = ESteps.Done;
SetError($"FileIO is not supported for builtin path: '{_options.FilePath}'.");
return;
}
if (FileUtility.IsFileIOSupported(_options.FilePath) == false)
{
_steps = ESteps.Done;
SetError($"FileIO is not supported for builtin path: '{_options.FilePath}'.");
return;
LoadResult result = LoadFromFile();
if (result.Succeeded == false)
{
_steps = ESteps.Done;
SetError(result.Error);
return;
}
}
LoadResult result = ParseArchiveFile();
if (result.Succeeded == false)
else
{
_steps = ESteps.Done;
SetError(result.Error);
return;
var decryptor = _options.ArchiveBundleDecryptor;
if (decryptor == null)
{
_steps = ESteps.Done;
SetError($"{_options.CacheName} archive bundle decryptor is null.");
return;
}
LoadResult result;
if (decryptor is IBundleMemoryDecryptor memoryDecryptor)
{
result = LoadFromMemory(memoryDecryptor);
}
else
{
_steps = ESteps.Done;
SetError($"{_options.CacheName} does not support '{decryptor.GetType().Name}' for ArchiveBundle.");
return;
}
if (result.Succeeded == false)
{
_steps = ESteps.Done;
SetError(result.Error);
return;
}
}
_steps = ESteps.CheckResult;
@@ -83,16 +108,33 @@ namespace YooAsset
ExecuteBatch();
}
private LoadResult ParseArchiveFile()
private LoadResult LoadFromFile()
{
try
{
_archiveBundle = ArchiveBundleHelper.LoadArchiveBundle(_options.FilePath);
_archiveBundle = ArchiveBundleHelper.LoadFromFile(_options.FilePath);
return LoadResult.Default();
}
catch (Exception ex)
{
return LoadResult.Failure($"Failed to parse archive file: {ex.Message}.");
return LoadResult.Failure($"Failed to load archive bundle file: {ex.Message}.");
}
}
private LoadResult LoadFromMemory(IBundleMemoryDecryptor decryptor)
{
try
{
var args = new BundleDecryptArgs(_options.Bundle, null, _options.FilePath);
byte[] binaryData = decryptor.GetDecryptedData(args);
if (binaryData == null)
return LoadResult.Failure($"{_options.CacheName} decryptor returned null data.");
_archiveBundle = ArchiveBundleHelper.LoadFromMemory(binaryData);
return LoadResult.Default();
}
catch (Exception ex)
{
return LoadResult.Failure($"Failed to load archive bundle file from memory: {ex.Message}.");
}
}
}

View File

@@ -21,11 +21,17 @@ namespace YooAsset
/// </summary>
public string FilePath { get; }
public LoadLocalArchiveBundleOptions(string cacheName, PackageBundle bundle, string filePath)
/// <summary>
/// ArchiveBundle 解密器
/// </summary>
public IBundleDecryptor ArchiveBundleDecryptor { get; }
public LoadLocalArchiveBundleOptions(string cacheName, PackageBundle bundle, string filePath, IBundleDecryptor archiveBundleDecryptor)
{
CacheName = cacheName;
Bundle = bundle;
FilePath = filePath;
ArchiveBundleDecryptor = archiveBundleDecryptor;
}
}
}

View File

@@ -56,14 +56,14 @@ namespace YooAsset
if (decryptor == null)
{
_steps = ESteps.Done;
SetError($"{_options.CacheName} decryptor is null.");
SetError($"{_options.CacheName} asset bundle decryptor is null.");
return;
}
LoadResult result;
if (decryptor is IBundleOffsetDecryptor offsetDecryptor)
{
result = LoadFromFileWithOffset(offsetDecryptor);
result = LoadFromFile(offsetDecryptor);
}
else if (decryptor is IBundleMemoryDecryptor memoryDecryptor)
{
@@ -76,7 +76,7 @@ namespace YooAsset
else
{
_steps = ESteps.Done;
SetError($"{_options.CacheName} does not support '{decryptor.GetType().Name}'.");
SetError($"{_options.CacheName} does not support '{decryptor.GetType().Name}' for AssetBundle.");
return;
}
@@ -137,18 +137,17 @@ namespace YooAsset
else
_createRequest = AssetBundle.LoadFromFileAsync(_options.FilePath);
}
private LoadResult LoadFromFileWithOffset(IBundleOffsetDecryptor decryptor)
private LoadResult LoadFromFile(IBundleOffsetDecryptor decryptor)
{
var args = new BundleDecryptArgs(_options.Bundle, null, _options.FilePath);
long rawOffset = decryptor.GetFileOffset(args);
if (rawOffset < 0)
return LoadResult.Failure($"{_options.CacheName} decryptor returned negative offset: {rawOffset}.");
ulong offset = (ulong)rawOffset;
long offset = decryptor.GetFileOffset(args);
if (offset < 0)
return LoadResult.Failure($"{_options.CacheName} decryptor returned negative offset: {offset}.");
if (IsWaitForCompletion)
_assetBundle = AssetBundle.LoadFromFile(_options.FilePath, 0, offset);
_assetBundle = AssetBundle.LoadFromFile(_options.FilePath, 0, (ulong)offset);
else
_createRequest = AssetBundle.LoadFromFileAsync(_options.FilePath, 0, offset);
_createRequest = AssetBundle.LoadFromFileAsync(_options.FilePath, 0, (ulong)offset);
return LoadResult.Default();
}

View File

@@ -1,5 +1,4 @@
using System;
using System.IO;
namespace YooAsset
{
@@ -62,7 +61,7 @@ namespace YooAsset
if (decryptor == null)
{
_steps = ESteps.Done;
SetError($"{_options.CacheName} decryptor is null.");
SetError($"{_options.CacheName} raw bundle decryptor is null.");
return;
}
@@ -74,7 +73,7 @@ namespace YooAsset
else
{
_steps = ESteps.Done;
SetError($"{_options.CacheName} does not support '{decryptor.GetType().Name}'.");
SetError($"{_options.CacheName} does not support '{decryptor.GetType().Name}' for RawBundle.");
return;
}
@@ -113,13 +112,12 @@ namespace YooAsset
{
try
{
byte[] data = File.ReadAllBytes(_options.FilePath);
_rawBundle = new RawBundle(data);
_rawBundle = RawBundleHelper.LoadFromFile(_options.FilePath);
return LoadResult.Default();
}
catch (Exception ex)
{
return LoadResult.Failure($"Failed to read raw bundle file: {ex.Message}.");
return LoadResult.Failure($"Failed to load raw bundle file: {ex.Message}.");
}
}
private LoadResult LoadFromMemory(IBundleMemoryDecryptor decryptor)
@@ -129,7 +127,7 @@ namespace YooAsset
if (binaryData == null)
return LoadResult.Failure($"{_options.CacheName} decryptor returned null data.");
_rawBundle = new RawBundle(binaryData);
_rawBundle = RawBundleHelper.LoadFromMemory(binaryData);
return LoadResult.Default();
}
}

View File

@@ -199,12 +199,12 @@ namespace YooAsset
if (binaryData == null)
return LoadResult.Failure($"{_options.CacheName} decryptor returned null data.");
_rawBundle = new RawBundle(binaryData);
_rawBundle = RawBundleHelper.LoadFromMemory(binaryData);
return LoadResult.Default();
}
else
{
_rawBundle = new RawBundle(fileData);
_rawBundle = RawBundleHelper.LoadFromMemory(fileData);
return LoadResult.Default();
}
}

View File

@@ -20,15 +20,22 @@ namespace YooAsset
/// </summary>
public IBundleDecryptor RawBundleDecryptor { get; }
/// <summary>
/// ArchiveBundle 解密器
/// </summary>
public IBundleDecryptor ArchiveBundleDecryptor { get; }
/// <summary>
/// 下载后台
/// </summary>
public IDownloadBackend DownloadBackend { get; }
public Configuration(IBundleDecryptor assetBundleDecryptor, IBundleDecryptor rawBundleDecryptor, IDownloadBackend downloadBackend)
public Configuration(IBundleDecryptor assetBundleDecryptor, IBundleDecryptor rawBundleDecryptor,
IBundleDecryptor archiveBundleDecryptor, IDownloadBackend downloadBackend)
{
AssetBundleDecryptor = assetBundleDecryptor;
RawBundleDecryptor = rawBundleDecryptor;
ArchiveBundleDecryptor = archiveBundleDecryptor;
DownloadBackend = downloadBackend;
}
}

View File

@@ -60,7 +60,8 @@ namespace YooAsset
var options = new LoadLocalArchiveBundleOptions(
cacheName: _fileCache.GetType().Name,
bundle: _bundle,
filePath: _cacheEntry.FilePath);
filePath: _cacheEntry.FilePath,
archiveBundleDecryptor: _fileCache.Config.ArchiveBundleDecryptor);
_loadLocalArchiveBundleOp = new LoadLocalArchiveBundleOperation(options);
_loadLocalArchiveBundleOp.StartOperation();
AddChildOperation(_loadLocalArchiveBundleOp);

View File

@@ -1,5 +1,4 @@
using System;
using System.IO;
namespace YooAsset
{
@@ -22,8 +21,7 @@ namespace YooAsset
try
{
byte[] data = File.ReadAllBytes(editorFilePath);
var rawBundle = new RawBundle(data);
var rawBundle = RawBundleHelper.LoadFromFile(editorFilePath);
SetResult();
BundleHandle = new VirtualRawBundleHandle(_bundle, rawBundle);

View File

@@ -61,7 +61,8 @@ namespace YooAsset
var options = new LoadLocalArchiveBundleOptions(
cacheName: _fileCache.GetType().Name,
bundle: _bundle,
filePath: _cacheEntry.DataFilePath);
filePath: _cacheEntry.DataFilePath,
archiveBundleDecryptor: _fileCache.Config.ArchiveBundleDecryptor);
_loadLocalArchiveBundleOp = new LoadLocalArchiveBundleOperation(options);
_loadLocalArchiveBundleOp.StartOperation();
AddChildOperation(_loadLocalArchiveBundleOp);

View File

@@ -30,18 +30,25 @@ namespace YooAsset
/// </summary>
public IBundleDecryptor RawBundleDecryptor { get; }
/// <summary>
/// ArchiveBundle 解密器
/// </summary>
public IBundleDecryptor ArchiveBundleDecryptor { get; }
/// <summary>
/// AssetBundle 备用解密器
/// </summary>
public IBundleMemoryDecryptor AssetBundleFallbackDecryptor { get; }
public Configuration(int fileVerifyMaxConcurrency, EFileVerifyLevel fileVerifyLevel,
IBundleDecryptor assetBundleDecryptor, IBundleDecryptor rawBundleDecryptor, IBundleMemoryDecryptor assetBundleFallbackDecryptor)
IBundleDecryptor assetBundleDecryptor, IBundleDecryptor rawBundleDecryptor,
IBundleDecryptor archiveBundleDecryptor, IBundleMemoryDecryptor assetBundleFallbackDecryptor)
{
FileVerifyMaxConcurrency = fileVerifyMaxConcurrency;
FileVerifyLevel = fileVerifyLevel;
AssetBundleDecryptor = assetBundleDecryptor;
RawBundleDecryptor = rawBundleDecryptor;
ArchiveBundleDecryptor = archiveBundleDecryptor;
AssetBundleFallbackDecryptor = assetBundleFallbackDecryptor;
}
}

View File

@@ -56,15 +56,14 @@ namespace YooAsset
}
private readonly string _archiveFilePath;
private readonly byte[] _memoryData;
private readonly Dictionary<string, FileEntry> _entries;
private readonly Dictionary<string, RawFileObject> _cachedObjects = new Dictionary<string, RawFileObject>();
private bool _isUnloaded;
/// <summary>
/// 创建 ArchiveBundle 实例
/// 从本地文件创建 ArchiveBundle 实例
/// </summary>
/// <param name="archiveFilePath">归档文件的本地路径</param>
/// <param name="entries">子文件索引字典</param>
public ArchiveBundle(string archiveFilePath, Dictionary<string, FileEntry> entries)
{
if (string.IsNullOrEmpty(archiveFilePath))
@@ -73,6 +72,23 @@ namespace YooAsset
throw new ArgumentNullException(nameof(entries));
_archiveFilePath = archiveFilePath;
_memoryData = null;
_entries = entries;
_isUnloaded = false;
}
/// <summary>
/// 从解密后的内存数据创建 ArchiveBundle 实例
/// </summary>
public ArchiveBundle(byte[] memoryData, Dictionary<string, FileEntry> entries)
{
if (memoryData == null)
throw new ArgumentNullException(nameof(memoryData));
if (entries == null)
throw new ArgumentNullException(nameof(entries));
_archiveFilePath = null;
_memoryData = memoryData;
_entries = entries;
_isUnloaded = false;
}
@@ -92,8 +108,8 @@ namespace YooAsset
if (_cachedObjects.TryGetValue(assetPath, out RawFileObject cached))
return cached;
byte[] fileData = ReadFileData(assetPath);
var rawFileObject = RawFileObject.CreateFromBytes(fileData);
byte[] assetData = ReadAssetData(assetPath);
var rawFileObject = RawFileObject.CreateFromBytes(assetData);
_cachedObjects[assetPath] = rawFileObject;
return rawFileObject;
}
@@ -113,14 +129,24 @@ namespace YooAsset
_entries.Clear();
}
/// <summary>
/// 从归档文件中读取子文件的字节数据
/// </summary>
private byte[] ReadFileData(string assetPath)
private byte[] ReadAssetData(string assetPath)
{
if (_entries.TryGetValue(assetPath, out FileEntry entry) == false)
throw new InvalidOperationException($"Asset not found in archive: '{assetPath}'.");
if (_memoryData != null)
return ReadFromMemory(entry);
else
return ReadFromFile(entry);
}
private byte[] ReadFromMemory(FileEntry entry)
{
byte[] buffer = new byte[entry.DataLength];
Buffer.BlockCopy(_memoryData, (int)entry.DataOffset, buffer, 0, (int)entry.DataLength);
return buffer;
}
private byte[] ReadFromFile(FileEntry entry)
{
byte[] buffer = new byte[entry.DataLength];
using (var fs = new FileStream(_archiveFilePath, FileMode.Open, FileAccess.Read, FileShare.Read))
{
@@ -130,7 +156,7 @@ namespace YooAsset
{
int read = fs.Read(buffer, bytesRead, buffer.Length - bytesRead);
if (read == 0)
throw new EndOfStreamException($"Unexpected end of archive file while reading '{assetPath}'.");
throw new EndOfStreamException($"Unexpected end of archive file while reading '{entry.AssetPath}'.");
bytesRead += read;
}
}

View File

@@ -8,14 +8,36 @@ namespace YooAsset
internal static class ArchiveBundleHelper
{
/// <summary>
/// 解析 YARK 归档文件
/// 从本地文件解析 YARK 归档
/// </summary>
/// <param name="filePath">归档文件路径</param>
/// <returns>解析成功的 ArchiveBundle 实例</returns>
public static ArchiveBundle LoadArchiveBundle(string filePath)
public static ArchiveBundle LoadFromFile(string filePath)
{
using (var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
using (var reader = new BinaryReader(fs))
{
var entries = ParseEntries(fs, fs.Length);
return new ArchiveBundle(filePath, entries);
}
}
/// <summary>
/// 从解密后的内存数据解析 YARK 归档
/// </summary>
/// <param name="fileData">解密后的完整归档字节数据</param>
/// <returns>解析成功的 ArchiveBundle 实例</returns>
public static ArchiveBundle LoadFromMemory(byte[] fileData)
{
using (var ms = new MemoryStream(fileData, false))
{
var entries = ParseEntries(ms, fileData.Length);
return new ArchiveBundle(fileData, entries);
}
}
private static Dictionary<string, ArchiveBundle.FileEntry> ParseEntries(Stream stream, long dataLength)
{
using (var reader = new BinaryReader(stream, Encoding.UTF8, true))
{
// 校验文件头魔数YARK
uint magic = reader.ReadUInt32();
@@ -34,7 +56,6 @@ namespace YooAsset
if (fileCount > ArchiveBundleConsts.MaxChildFileCount)
throw new InvalidOperationException($"Archive child file count {fileCount} exceeds maximum ({ArchiveBundleConsts.MaxChildFileCount}).");
long fileLength = fs.Length;
var entries = new Dictionary<string, ArchiveBundle.FileEntry>(fileCount);
for (int i = 0; i < fileCount; i++)
{
@@ -44,9 +65,9 @@ namespace YooAsset
throw new InvalidOperationException($"Invalid path length {pathLen} at entry index {i}.");
if (pathLen > ArchiveBundleConsts.MaxChildFilePathBytes)
throw new InvalidOperationException($"Path length {pathLen} exceeds maximum ({ArchiveBundleConsts.MaxChildFilePathBytes}) at entry index {i}.");
long remaining = fileLength - fs.Position;
long remaining = dataLength - stream.Position;
if (pathLen > remaining)
throw new InvalidOperationException($"Path length {pathLen} exceeds remaining file size at entry index {i}.");
throw new InvalidOperationException($"Path length {pathLen} exceeds remaining data size at entry index {i}.");
string assetPath = Encoding.UTF8.GetString(reader.ReadBytes(pathLen));
if (string.IsNullOrEmpty(assetPath))
@@ -59,15 +80,15 @@ namespace YooAsset
uint crc = reader.ReadUInt32();
// 校验数据范围是否越过文件边界
if (offset < 0 || offset > fileLength)
if (offset < 0 || offset > dataLength)
throw new InvalidOperationException($"Invalid data offset {offset} for '{assetPath}'.");
if (length < 0 || length > fileLength - offset)
throw new InvalidOperationException($"Data range [{offset}, {offset + length}) exceeds file size {fileLength} for '{assetPath}'.");
if (length < 0 || length > dataLength - offset)
throw new InvalidOperationException($"Data range [{offset}, {offset + length}) exceeds data size {dataLength} for '{assetPath}'.");
entries[assetPath] = new ArchiveBundle.FileEntry(assetPath, offset, length, crc);
}
return new ArchiveBundle(filePath, entries);
return entries;
}
}
}

View File

@@ -0,0 +1,24 @@
using System.IO;
namespace YooAsset
{
internal static class RawBundleHelper
{
/// <summary>
/// 从本地文件加载 RawBundle
/// </summary>
public static RawBundle LoadFromFile(string filePath)
{
byte[] fileData = File.ReadAllBytes(filePath);
return new RawBundle(fileData);
}
/// <summary>
/// 从内存数据加载 RawBundle
/// </summary>
public static RawBundle LoadFromMemory(byte[] fileData)
{
return new RawBundle(fileData);
}
}
}

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 9d03e8f2341d4c74886f1e7aef83d0c6
guid: 37ec8a856caa44b4a993e52d4f1d6fb1
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@@ -109,17 +109,22 @@ namespace YooAsset
/// <summary>
/// AssetBundle 解密器 <see cref="IBundleDecryptor"/>
/// </summary>
AssetbundleDecryptor,
AssetBundleDecryptor,
/// <summary>
/// RawBundle 解密器 <see cref="IBundleDecryptor"/>
/// </summary>
RawbundleDecryptor,
RawBundleDecryptor,
/// <summary>
/// ArchiveBundle 解密器 <see cref="IBundleDecryptor"/>
/// </summary>
ArchiveBundleDecryptor,
/// <summary>
/// AssetBundle 备用解密器 <see cref="IBundleMemoryDecryptor"/>
/// </summary>
AssetbundleFallbackDecryptor,
AssetBundleFallbackDecryptor,
/// <summary>
/// 资源清单解密器 <see cref="IManifestDecryptor"/>

View File

@@ -131,6 +131,11 @@ namespace YooAsset
/// </summary>
public IBundleDecryptor RawBundleDecryptor { get; private set; }
/// <summary>
/// 自定义参数ArchiveBundle 解密器
/// </summary>
public IBundleDecryptor ArchiveBundleDecryptor { get; private set; }
/// <summary>
/// 自定义参数AssetBundle 备用解密器
/// </summary>
@@ -278,15 +283,19 @@ namespace YooAsset
// 限制在合理范围内1-32
UnpackMaxRequestsPerFrame = Mathf.Clamp(convertValue, 1, 32);
}
else if (paramName == nameof(EFileSystemParameter.AssetbundleDecryptor))
else if (paramName == nameof(EFileSystemParameter.AssetBundleDecryptor))
{
AssetBundleDecryptor = FileSystemHelper.CastParameter<IBundleDecryptor>(paramName, value);
}
else if (paramName == nameof(EFileSystemParameter.RawbundleDecryptor))
else if (paramName == nameof(EFileSystemParameter.RawBundleDecryptor))
{
RawBundleDecryptor = FileSystemHelper.CastParameter<IBundleDecryptor>(paramName, value);
}
else if (paramName == nameof(EFileSystemParameter.AssetbundleFallbackDecryptor))
else if (paramName == nameof(EFileSystemParameter.ArchiveBundleDecryptor))
{
ArchiveBundleDecryptor = FileSystemHelper.CastParameter<IBundleDecryptor>(paramName, value);
}
else if (paramName == nameof(EFileSystemParameter.AssetBundleFallbackDecryptor))
{
AssetBundleFallbackDecryptor = FileSystemHelper.CastParameter<IBundleMemoryDecryptor>(paramName, value);
}
@@ -340,6 +349,7 @@ namespace YooAsset
var cacheConfig = new BuiltinBundleCache.Configuration(
assetBundleDecryptor: AssetBundleDecryptor,
rawBundleDecryptor: RawBundleDecryptor,
archiveBundleDecryptor: ArchiveBundleDecryptor,
downloadBackend: DownloadBackend);
BuiltinBundleCache = new BuiltinBundleCache(packageName, _packageRoot, cacheConfig);
}
@@ -351,6 +361,7 @@ namespace YooAsset
fileVerifyLevel: FileVerifyLevel,
assetBundleDecryptor: AssetBundleDecryptor,
rawBundleDecryptor: RawBundleDecryptor,
archiveBundleDecryptor: ArchiveBundleDecryptor,
assetBundleFallbackDecryptor: AssetBundleFallbackDecryptor);
UnpackBundleCache = new SandboxBundleCache(packageName, _unpackBundleFilesRoot, cacheConfig);
}

View File

@@ -57,7 +57,7 @@ namespace YooAsset
if (_steps == ESteps.TryCopyFile)
{
// 注意Android/OpenHarmony 平台 File.Exists 无法识别包体内文件。
// 该步骤仅对 StreamingAssets 为真实文件系统目录的平台Windows/iOS/Mac生效。
// 说明:该步骤仅对 StreamingAssets 为真实文件系统目录的平台Windows/iOS/Mac生效。
if (File.Exists(_sourceFilePath))
{
try

View File

@@ -123,6 +123,11 @@ namespace YooAsset
/// </summary>
public IBundleDecryptor RawBundleDecryptor { get; private set; }
/// <summary>
/// 自定义参数ArchiveBundle 解密器
/// </summary>
public IBundleDecryptor ArchiveBundleDecryptor { get; private set; }
/// <summary>
/// 自定义参数AssetBundle 备用解密器
/// </summary>
@@ -275,15 +280,19 @@ namespace YooAsset
{
RemoteService = FileSystemHelper.CastParameter<IRemoteService>(paramName, value);
}
else if (paramName == nameof(EFileSystemParameter.AssetbundleDecryptor))
else if (paramName == nameof(EFileSystemParameter.AssetBundleDecryptor))
{
AssetBundleDecryptor = FileSystemHelper.CastParameter<IBundleDecryptor>(paramName, value);
}
else if (paramName == nameof(EFileSystemParameter.RawbundleDecryptor))
else if (paramName == nameof(EFileSystemParameter.RawBundleDecryptor))
{
RawBundleDecryptor = FileSystemHelper.CastParameter<IBundleDecryptor>(paramName, value);
}
else if (paramName == nameof(EFileSystemParameter.AssetbundleFallbackDecryptor))
else if (paramName == nameof(EFileSystemParameter.ArchiveBundleDecryptor))
{
ArchiveBundleDecryptor = FileSystemHelper.CastParameter<IBundleDecryptor>(paramName, value);
}
else if (paramName == nameof(EFileSystemParameter.AssetBundleFallbackDecryptor))
{
AssetBundleFallbackDecryptor = FileSystemHelper.CastParameter<IBundleMemoryDecryptor>(paramName, value);
}
@@ -337,6 +346,7 @@ namespace YooAsset
fileVerifyLevel: FileVerifyLevel,
assetBundleDecryptor: AssetBundleDecryptor,
rawBundleDecryptor: RawBundleDecryptor,
archiveBundleDecryptor: ArchiveBundleDecryptor,
assetBundleFallbackDecryptor: AssetBundleFallbackDecryptor);
BundleCache = new SandboxBundleCache(PackageName, _cacheBundleFilesRoot, cacheConfig);
}

View File

@@ -148,11 +148,11 @@ namespace YooAsset
{
RemoteService = FileSystemHelper.CastParameter<IRemoteService>(paramName, value);
}
else if (paramName == nameof(EFileSystemParameter.AssetbundleDecryptor))
else if (paramName == nameof(EFileSystemParameter.AssetBundleDecryptor))
{
AssetBundleDecryptor = FileSystemHelper.CastParameter<IBundleDecryptor>(paramName, value);
}
else if (paramName == nameof(EFileSystemParameter.RawbundleDecryptor))
else if (paramName == nameof(EFileSystemParameter.RawBundleDecryptor))
{
RawBundleDecryptor = FileSystemHelper.CastParameter<IBundleDecryptor>(paramName, value);
}

View File

@@ -158,11 +158,11 @@ namespace YooAsset
{
DownloadVerifyLevel = FileSystemHelper.CastParameter<EFileVerifyLevel>(paramName, value);
}
else if (paramName == nameof(EFileSystemParameter.AssetbundleDecryptor))
else if (paramName == nameof(EFileSystemParameter.AssetBundleDecryptor))
{
AssetBundleDecryptor = FileSystemHelper.CastParameter<IBundleDecryptor>(paramName, value);
}
else if (paramName == nameof(EFileSystemParameter.RawbundleDecryptor))
else if (paramName == nameof(EFileSystemParameter.RawBundleDecryptor))
{
RawBundleDecryptor = FileSystemHelper.CastParameter<IBundleDecryptor>(paramName, value);
}

View File

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

View File

@@ -0,0 +1,122 @@
using System;
using System.IO;
using YooAsset;
/// <summary>
/// Bundle 加密和解密示例
/// 注意:示例代码仅演示接口用法,实际项目请替换密钥和加密算法。
/// </summary>
public static class BundleEncryptionSample
{
private const byte XOR_KEY = 64;
private const int FILE_OFFSET = 32;
/// <summary>
/// 内存模式加密器:将加密后的完整文件数据返回给构建管线。
/// </summary>
public class MemoryEncryptor : IBundleEncryptor
{
public BundleEncryptResult Encrypt(BundleEncryptArgs args)
{
byte[] data = File.ReadAllBytes(args.FilePath);
Xor(data);
return new BundleEncryptResult(true, data);
}
}
/// <summary>
/// 内存模式解密器:运行时返回解密后的完整文件数据。
/// </summary>
public class MemoryDecryptor : IBundleMemoryDecryptor
{
byte[] IBundleMemoryDecryptor.GetDecryptedData(BundleDecryptArgs args)
{
byte[] data = args.FileData;
if (data == null)
data = FileUtility.ReadAllBytes(args.FilePath);
Xor(data);
return data;
}
}
/// <summary>
/// 偏移模式加密器:在文件头部插入固定长度的数据。
/// </summary>
public class OffsetEncryptor : IBundleEncryptor
{
public BundleEncryptResult Encrypt(BundleEncryptArgs args)
{
byte[] data = File.ReadAllBytes(args.FilePath);
byte[] encryptedData = new byte[data.Length + FILE_OFFSET];
Buffer.BlockCopy(data, 0, encryptedData, FILE_OFFSET, data.Length);
return new BundleEncryptResult(true, encryptedData);
}
}
/// <summary>
/// 偏移模式解密器:运行时返回真实 Bundle 数据起始偏移。
/// </summary>
public class OffsetDecryptor : IBundleOffsetDecryptor
{
long IBundleOffsetDecryptor.GetFileOffset(BundleDecryptArgs args)
{
return FILE_OFFSET;
}
}
/// <summary>
/// 流模式加密器:示例中使用 XOR 加密整个文件。
/// </summary>
public class StreamEncryptor : IBundleEncryptor
{
public BundleEncryptResult Encrypt(BundleEncryptArgs args)
{
byte[] data = File.ReadAllBytes(args.FilePath);
Xor(data);
return new BundleEncryptResult(true, data);
}
}
/// <summary>
/// 流模式解密器:运行时返回一个读取时自动解密的文件流。
/// </summary>
public class StreamDecryptor : IBundleStreamDecryptor
{
Stream IBundleStreamDecryptor.CreateDecryptionStream(BundleDecryptArgs args)
{
return new XorFileStream(args.FilePath, FileMode.Open, FileAccess.Read, FileShare.Read);
}
int IBundleStreamDecryptor.GetBufferSize(BundleDecryptArgs args)
{
return 1024;
}
}
private class XorFileStream : FileStream
{
public XorFileStream(string path, FileMode mode, FileAccess access, FileShare share)
: base(path, mode, access, share)
{
}
public override int Read(byte[] array, int offset, int count)
{
int read = base.Read(array, offset, count);
for (int i = offset; i < offset + read; i++)
{
array[i] ^= XOR_KEY;
}
return read;
}
}
private static void Xor(byte[] data)
{
for (int i = 0; i < data.Length; i++)
{
data[i] ^= XOR_KEY;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9d21463d1d8d4a72a4f7e929b2f7a325
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -71,7 +71,7 @@ public static class TestPackageBuilder
buildParameters.ClearBuildCacheFiles = true;
buildParameters.UseAssetDependencyDB = true;
buildParameters.BuiltinShadersBundleName = builtinShaderBundleName;
buildParameters.BundleEncryptor = new TestFileStreamEncryption();
buildParameters.BundleEncryptor = new TestAssetBundleEncryptor();
buildParameters.ManifestEncryptor = new TestManifestEncryptor();
buildParameters.ManifestDecryptor = new TestManifestDecryptor();
@@ -115,7 +115,7 @@ public static class TestPackageBuilder
buildParameters.CompressOption = ECompressOption.LZ4;
buildParameters.ClearBuildCacheFiles = true;
buildParameters.UseAssetDependencyDB = true;
buildParameters.BundleEncryptor = new TestFileStreamEncryption();
buildParameters.BundleEncryptor = new TestAssetBundleEncryptor();
buildParameters.ManifestEncryptor = new TestManifestEncryptor();
buildParameters.ManifestDecryptor = new TestManifestDecryptor();
@@ -157,6 +157,7 @@ public static class TestPackageBuilder
buildParameters.BundledCopyParams = string.Empty;
buildParameters.ClearBuildCacheFiles = true;
buildParameters.UseAssetDependencyDB = true;
buildParameters.BundleEncryptor = new TestRawBundleEncryptor();
var pipeline = new RawFileBuildPipeline();
BuildResult buildResult = pipeline.Run(buildParameters, false);
@@ -197,6 +198,7 @@ public static class TestPackageBuilder
buildParameters.ClearBuildCacheFiles = true;
buildParameters.UseAssetDependencyDB = true;
buildParameters.FileAlignment = 4;
buildParameters.BundleEncryptor = new TestArchiveBundleEncryptor();
var pipeline = new ArchiveFileBuildPipeline();
BuildResult buildResult = pipeline.Run(buildParameters, false);

View File

@@ -14,6 +14,7 @@ using YooAsset;
/// 2. 同步加载归档子文件,验证 GetBytes() 和 GetText() 均返回有效数据archive_file_b
/// 3. 重复加载同一归档子文件验证缓存命中不会失败archive_file_c
/// 4. 释放句柄并卸载后重新加载验证卸载保护和重载链路正常archive_file_e
/// 5. 加载加密归档子文件,验证 Memory 解密路径archive_file_x / archive_file_y
/// </remarks>
public class TestLoadArchiveBundle
{
@@ -103,5 +104,44 @@ public class TestLoadArchiveBundle
Assert.AreNotSame(previousObj, reloadedObj);
reloadHandle.Release();
}
// 异步加载加密归档子文件,验证 Memory 解密路径
{
var assetHandle = package.LoadAssetAsync<RawFileObject>("archive_file_x");
yield return assetHandle;
Assert.AreEqual(EOperationStatus.Succeeded, assetHandle.Status);
var rawFileObject = assetHandle.GetAssetObject<RawFileObject>();
Assert.IsNotNull(rawFileObject);
byte[] fileBytes = rawFileObject.GetBytes();
Assert.IsNotNull(fileBytes);
Assert.Greater(fileBytes.Length, 0);
string fileText = rawFileObject.GetText();
Assert.IsNotNull(fileText);
Assert.IsNotEmpty(fileText);
Assert.AreEqual("this is archive file x !", fileText);
assetHandle.Release();
}
// 同步加载加密归档子文件,验证 Memory 解密路径
{
var assetHandle = package.LoadAssetSync<RawFileObject>("archive_file_y");
Assert.AreEqual(EOperationStatus.Succeeded, assetHandle.Status);
var rawFileObject = assetHandle.GetAssetObject<RawFileObject>();
Assert.IsNotNull(rawFileObject);
byte[] fileBytes = rawFileObject.GetBytes();
Assert.IsNotNull(fileBytes);
Assert.Greater(fileBytes.Length, 0);
string fileText = rawFileObject.GetText();
Assert.IsNotNull(fileText);
Assert.IsNotEmpty(fileText);
Assert.AreEqual("this is archive file y !", fileText);
assetHandle.Release();
}
}
}

View File

@@ -4,15 +4,17 @@ using NUnit.Framework;
using YooAsset;
/// <summary>
/// 测试 RawFileObject 加载
/// 测试 RawBundle 加载
/// </summary>
/// <remarks>
/// 覆盖 API: LoadAssetAsync(RawFileObject) / LoadAssetSync(RawFileObject)
/// 测试内容:
/// 1. 异步通过 RawFileObject 加载,验证 GetBytes() 和 GetText() 均返回有效数据raw_file_c
/// 2. 同步通过 RawFileObject 加载,验证 GetBytes() 和 GetText() 均返回有效数据raw_file_d
/// 3. 异步加载加密 RawBundle验证 Memory 解密路径raw_file_x
/// 4. 同步加载加密 RawBundle验证 Memory 解密路径raw_file_y
/// </remarks>
public class TestLoadRawFileObject
public class TestLoadRawBundle
{
public IEnumerator RuntimeTester()
{
@@ -55,5 +57,44 @@ public class TestLoadRawFileObject
Assert.IsNotEmpty(fileText);
assetHandle.Release();
}
// 测试异步加载加密 RawBundle通过 RawFileObject 获取二进制数据和文本数据
{
var assetHandle = package.LoadAssetAsync<RawFileObject>("raw_file_x");
yield return assetHandle;
Assert.AreEqual(EOperationStatus.Succeeded, assetHandle.Status);
var rawFileObject = assetHandle.GetAssetObject<RawFileObject>();
Assert.IsNotNull(rawFileObject);
byte[] fileBytes = rawFileObject.GetBytes();
Assert.IsNotNull(fileBytes);
Assert.Greater(fileBytes.Length, 0);
string fileText = rawFileObject.GetText();
Assert.IsNotNull(fileText);
Assert.IsNotEmpty(fileText);
Assert.AreEqual("this is raw file x !", fileText);
assetHandle.Release();
}
// 测试同步加载加密 RawBundle通过 RawFileObject 获取二进制数据和文本数据
{
var assetHandle = package.LoadAssetSync<RawFileObject>("raw_file_y");
Assert.AreEqual(EOperationStatus.Succeeded, assetHandle.Status);
var rawFileObject = assetHandle.GetAssetObject<RawFileObject>();
Assert.IsNotNull(rawFileObject);
byte[] fileBytes = rawFileObject.GetBytes();
Assert.IsNotNull(fileBytes);
Assert.Greater(fileBytes.Length, 0);
string fileText = rawFileObject.GetText();
Assert.IsNotNull(fileText);
Assert.IsNotEmpty(fileText);
Assert.AreEqual("this is raw file y !", fileText);
assetHandle.Release();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c281ef07d441fc9458a3fa7be72b24c5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -14,7 +14,7 @@ using YooAsset;
/// 覆盖 API: LoadAssetAsync (SpriteAtlas)
/// 测试内容:
/// 1. 异步加载 SpriteAtlas 资源,验证加载状态和资源对象非空
/// 2. 从图集中获取三个精灵(bullet、pause、rocket),验证均非空
/// 2. 从图集中获取三个精灵(sprite_a、sprite_b、sprite_c),验证均非空
/// </remarks>
public class TestLoadSpriteAtlas
{
@@ -30,13 +30,13 @@ public class TestLoadSpriteAtlas
var spriteAtlas = assetHandle.AssetObject as SpriteAtlas;
Assert.IsNotNull(spriteAtlas);
var sprite1 = spriteAtlas.GetSprite("bullet");
var sprite1 = spriteAtlas.GetSprite("sprite_a");
Assert.IsNotNull(sprite1);
var sprite2 = spriteAtlas.GetSprite("pause");
var sprite2 = spriteAtlas.GetSprite("sprite_b");
Assert.IsNotNull(sprite2);
var sprite3 = spriteAtlas.GetSprite("rocket");
var sprite3 = spriteAtlas.GetSprite("sprite_c");
Assert.IsNotNull(sprite3);
assetHandle.Release();
}

View File

@@ -0,0 +1,20 @@
using YooAsset;
/// <summary>
/// ArchiveBundle 解密器
/// </summary>
public class TestArchiveBundleDecryptor : IBundleMemoryDecryptor
{
byte[] IBundleMemoryDecryptor.GetDecryptedData(BundleDecryptArgs args)
{
byte[] data = args.FileData;
if (data == null)
data = FileUtility.ReadAllBytes(args.FilePath);
for (int i = 0; i < data.Length; i++)
{
data[i] ^= TestArchiveBundleEncryptor.KEY;
}
return data;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2ae99256961d47c09ef5a633f5d48c68
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,24 @@
using System.IO;
using YooAsset;
/// <summary>
/// ArchiveBundle 加密器
/// </summary>
public class TestArchiveBundleEncryptor : IBundleEncryptor
{
public const byte KEY = 0x5A;
public BundleEncryptResult Encrypt(BundleEncryptArgs fileInfo)
{
string bundleName = fileInfo.BundleName.ToLowerInvariant();
if (bundleName.Contains("_testres6_encryptfiles_") == false)
return new BundleEncryptResult(false, null);
byte[] fileData = File.ReadAllBytes(fileInfo.FilePath);
for (int i = 0; i < fileData.Length; i++)
{
fileData[i] ^= KEY;
}
return new BundleEncryptResult(true, fileData);
}
}

View File

@@ -0,0 +1,20 @@
using YooAsset;
/// <summary>
/// AssetBundle 解密器
/// </summary>
public class TestAssetBundleDecryptor : IBundleMemoryDecryptor
{
byte[] IBundleMemoryDecryptor.GetDecryptedData(BundleDecryptArgs args)
{
byte[] data = args.FileData;
if (data == null)
data = FileUtility.ReadAllBytes(args.FilePath);
for (int i = 0; i < data.Length; i++)
{
data[i] ^= TestAssetBundleEncryptor.KEY;
}
return data;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 502b8de579fc3e04ab0c61064b344b50
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,26 @@
using System.IO;
using YooAsset;
/// <summary>
/// AssetBundle 加密器
/// </summary>
public class TestAssetBundleEncryptor : IBundleEncryptor
{
public const byte KEY = 0x5A;
public BundleEncryptResult Encrypt(BundleEncryptArgs fileInfo)
{
string bundleName = fileInfo.BundleName.ToLowerInvariant();
if (bundleName.Contains("_testres_encryptfiles_") == false &&
bundleName.Contains("_testres3_importfiles_") == false &&
bundleName.Contains("_testres3_unpackfiles_") == false)
return new BundleEncryptResult(false, null);
byte[] fileData = File.ReadAllBytes(fileInfo.FilePath);
for (int i = 0; i < fileData.Length; i++)
{
fileData[i] ^= KEY;
}
return new BundleEncryptResult(true, fileData);
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 09c441a23eaa4be486ad910c9dcfa401
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,123 +0,0 @@
using System;
using System.IO;
using YooAsset;
/// <summary>
/// XOR 解密文件流,读取时自动对每个字节执行异或解密
/// </summary>
public class TestBundleStream : FileStream
{
public const byte KEY = 64;
public TestBundleStream(string path, FileMode mode, FileAccess access, FileShare share) : base(path, mode, access, share)
{
}
public TestBundleStream(string path, FileMode mode) : base(path, mode)
{
}
public override int Read(byte[] array, int offset, int count)
{
var index = base.Read(array, offset, count);
for (int i = 0; i < array.Length; i++)
{
array[i] ^= KEY;
}
return index;
}
}
/// <summary>
/// 流模式 XOR 加密器,对 TestRes3 目录的 Bundle 进行逐字节异或加密
/// </summary>
public class TestFileStreamEncryption : IBundleEncryptor
{
public BundleEncryptResult Encrypt(BundleEncryptArgs fileInfo)
{
// 说明对TestRes3资源目录进行加密
if (fileInfo.BundleName.Contains("_testres3_"))
{
var fileData = File.ReadAllBytes(fileInfo.FilePath);
for (int i = 0; i < fileData.Length; i++)
{
fileData[i] ^= TestBundleStream.KEY;
}
return new BundleEncryptResult(true, fileData);
}
else
{
return new BundleEncryptResult(false, null);
}
}
}
/// <summary>
/// 偏移模式加密器,在 Bundle 文件头部插入固定长度的空白偏移
/// </summary>
public class TestFileOffsetEncryption : IBundleEncryptor
{
public BundleEncryptResult Encrypt(BundleEncryptArgs fileInfo)
{
// 说明对TestRes3资源目录进行加密
if (fileInfo.BundleName.Contains("_testres3_"))
{
int offset = 32;
byte[] fileData = File.ReadAllBytes(fileInfo.FilePath);
var encryptedData = new byte[fileData.Length + offset];
Buffer.BlockCopy(fileData, 0, encryptedData, offset, fileData.Length);
return new BundleEncryptResult(true, encryptedData);
}
else
{
return new BundleEncryptResult(false, null);
}
}
}
/// <summary>
/// 偏移模式解密器,返回固定的文件偏移量以跳过加密头
/// </summary>
public class TestFileOffsetDecryption : IBundleOffsetDecryptor
{
private const long FILE_OFFSET = 32;
long IBundleOffsetDecryptor.GetFileOffset(BundleDecryptArgs args)
{
return FILE_OFFSET;
}
}
/// <summary>
/// 内存模式 XOR 解密器,将整个 Bundle 读入内存后逐字节异或解密
/// </summary>
public class TestFileMemoryDecryption : IBundleMemoryDecryptor
{
byte[] IBundleMemoryDecryptor.GetDecryptedData(BundleDecryptArgs args)
{
byte[] data = args.FileData;
// 注意:如果数据为空,自行加载文件数据。
if (data == null)
data = FileUtility.ReadAllBytes(args.FilePath);
for (int i = 0; i < data.Length; i++)
{
data[i] ^= TestBundleStream.KEY;
}
return data;
}
}
/// <summary>
/// 流模式解密器,返回 TestBundleStream 实现读取时自动解密
/// </summary>
public class TestFileStreamDecryption : IBundleStreamDecryptor
{
Stream IBundleStreamDecryptor.CreateDecryptionStream(BundleDecryptArgs args)
{
var fileStream = new TestBundleStream(args.FilePath, FileMode.Open, FileAccess.Read, FileShare.Read);
return fileStream;
}
int IBundleStreamDecryptor.GetBufferSize(BundleDecryptArgs args)
{
return 1024;
}
}

View File

@@ -0,0 +1,12 @@
using YooAsset;
/// <summary>
/// 资源清单解密器
/// </summary>
public class TestManifestDecryptor : IManifestDecryptor
{
byte[] IManifestDecryptor.Decrypt(byte[] fileData)
{
return TestXorCrypto.Crypto(fileData, TestManifestEncryptor.KEY);
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6c2d2ff26d5d4d4d9ac08ba6a420d3f1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,27 +1,17 @@
using System;
using System.Text;
using System.Collections;
using YooAsset;
/// <summary>
/// 测试用清单加密器,使用 XOR 方式加密清单数据
/// 资源清单加密器
/// </summary>
public class TestManifestEncryptor : IManifestEncryptor
{
public const string KEY = "YOO";
byte[] IManifestEncryptor.Encrypt(byte[] fileData)
{
return TestXorCrypto.Crypto(fileData, "YOO");
}
}
/// <summary>
/// 测试用清单解密器,使用 XOR 方式解密清单数据
/// </summary>
public class TestManifestDecryptor : IManifestDecryptor
{
byte[] IManifestDecryptor.Decrypt(byte[] fileData)
{
return TestXorCrypto.Crypto(fileData, "YOO");
return TestXorCrypto.Crypto(fileData, KEY);
}
}
@@ -62,7 +52,7 @@ public class TestXorCrypto
/// <returns>处理后的字节数组</returns>
public static byte[] Crypto(byte[] data, string key)
{
byte[] keyBytes = System.Text.Encoding.UTF8.GetBytes(key);
byte[] keyBytes = Encoding.UTF8.GetBytes(key);
return Crypto(data, keyBytes);
}
}
}

View File

@@ -0,0 +1,20 @@
using YooAsset;
/// <summary>
/// RawBundle 解密器
/// </summary>
public class TestRawBundleDecryptor : IBundleMemoryDecryptor
{
byte[] IBundleMemoryDecryptor.GetDecryptedData(BundleDecryptArgs args)
{
byte[] data = args.FileData;
if (data == null)
data = FileUtility.ReadAllBytes(args.FilePath);
for (int i = 0; i < data.Length; i++)
{
data[i] ^= TestRawBundleEncryptor.KEY;
}
return data;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7bf91e81ab6e85b4eb4f478fcd1e7261
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,24 @@
using System.IO;
using YooAsset;
/// <summary>
/// RawBundle 加密器
/// </summary>
public class TestRawBundleEncryptor : IBundleEncryptor
{
public const byte KEY = 0x6B;
public BundleEncryptResult Encrypt(BundleEncryptArgs fileInfo)
{
string bundleName = fileInfo.BundleName.ToLowerInvariant();
if (bundleName.Contains("_testres5_encryptfiles_") == false)
return new BundleEncryptResult(false, null);
byte[] fileData = File.ReadAllBytes(fileInfo.FilePath);
for (int i = 0; i < fileData.Length; i++)
{
fileData[i] ^= KEY;
}
return new BundleEncryptResult(true, fileData);
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d84195bebf934e44b8d73d46ba0ce838
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -170,26 +170,30 @@ public class T0_InitYooAssets : IPrebuildSetup, IPostBuildCleanup
{
var collector1 = new YooAsset.Editor.BundleCollector();
collector1.CollectPath = "";
collector1.CollectorGUID = "e082d492b9da65e499cee3495be3645d"; //TestRes3/encrypt目录
collector1.CollectorGUID = "e082d492b9da65e499cee3495be3645d"; //TestRes/EncryptFiles目录
collector1.CollectorType = YooAsset.Editor.ECollectorType.MainAssetCollector;
collector1.PackRuleName = nameof(YooAsset.Editor.PackSeparately);
YooAsset.Editor.BundleCollectorSettingData.CreateCollector(encryptGroup, collector1);
}
// 拷贝测试文件
var copyGroup = YooAsset.Editor.BundleCollectorSettingData.CreateGroup(testPackage, "CopyGroup");
{
var collector1 = new YooAsset.Editor.BundleCollector();
collector1.CollectPath = "";
collector1.CollectorGUID = "35f454fb80a715047bcf0ce30c7c4f18"; //TestRes3/ImportFiles目录
collector1.CollectorType = YooAsset.Editor.ECollectorType.MainAssetCollector;
collector1.AssetTags = "import";
collector1.PackRuleName = nameof(YooAsset.Editor.PackSeparately);
YooAsset.Editor.BundleCollectorSettingData.CreateCollector(copyGroup, collector1);
var collector2 = new YooAsset.Editor.BundleCollector();
collector2.CollectPath = "";
collector2.CollectorGUID = "35f454fb80a715047bcf0ce30c7c4f18"; //TestRes3/import目录
collector2.CollectorGUID = "401af1ca0abf3ae4594631e5f71bfe27"; //TestRes3/UnpackFiles目录
collector2.CollectorType = YooAsset.Editor.ECollectorType.MainAssetCollector;
collector2.AssetTags = "import";
collector2.AssetTags = "unpack";
collector2.PackRuleName = nameof(YooAsset.Editor.PackSeparately);
YooAsset.Editor.BundleCollectorSettingData.CreateCollector(encryptGroup, collector2);
var collector3 = new YooAsset.Editor.BundleCollector();
collector3.CollectPath = "";
collector3.CollectorGUID = "401af1ca0abf3ae4594631e5f71bfe27"; //TestRes3/unpack目录
collector3.CollectorType = YooAsset.Editor.ECollectorType.MainAssetCollector;
collector3.AssetTags = "unpack";
collector3.PackRuleName = nameof(YooAsset.Editor.PackSeparately);
YooAsset.Editor.BundleCollectorSettingData.CreateCollector(encryptGroup, collector3);
YooAsset.Editor.BundleCollectorSettingData.CreateCollector(copyGroup, collector2);
}
// 卸载测试文件
@@ -230,10 +234,17 @@ public class T0_InitYooAssets : IPrebuildSetup, IPostBuildCleanup
{
var collector1 = new YooAsset.Editor.BundleCollector();
collector1.CollectPath = "";
collector1.CollectorGUID = "fddaaf9430e24344196cc82ac3d006b4"; //TestRes/RawFiles目录
collector1.CollectorGUID = "fddaaf9430e24344196cc82ac3d006b4"; //TestRes5/RawFiles目录
collector1.CollectorType = YooAsset.Editor.ECollectorType.MainAssetCollector;
collector1.PackRuleName = nameof(YooAsset.Editor.PackRawFile);
YooAsset.Editor.BundleCollectorSettingData.CreateCollector(rawFileGroup, collector1);
var collector2 = new YooAsset.Editor.BundleCollector();
collector2.CollectPath = "";
collector2.CollectorGUID = "9378c52809165094cb532c1678355a3c"; //TestRes5/EncryptFiles目录
collector2.CollectorType = YooAsset.Editor.ECollectorType.MainAssetCollector;
collector2.PackRuleName = nameof(YooAsset.Editor.PackRawFile);
YooAsset.Editor.BundleCollectorSettingData.CreateCollector(rawFileGroup, collector2);
}
}
private static void CreateArchiveBundlePackageCollector()
@@ -247,10 +258,17 @@ public class T0_InitYooAssets : IPrebuildSetup, IPostBuildCleanup
{
var collector1 = new YooAsset.Editor.BundleCollector();
collector1.CollectPath = "";
collector1.CollectorGUID = "c0444018376a7cd4ead6a671035617d6"; //TestRes/ArchiveFiles目录
collector1.CollectorGUID = "c0444018376a7cd4ead6a671035617d6"; //TestRes6/ArchiveFiles目录
collector1.CollectorType = YooAsset.Editor.ECollectorType.MainAssetCollector;
collector1.PackRuleName = nameof(YooAsset.Editor.PackCollector);
YooAsset.Editor.BundleCollectorSettingData.CreateCollector(archiveFileGroup, collector1);
var collector2 = new YooAsset.Editor.BundleCollector();
collector2.CollectPath = "";
collector2.CollectorGUID = "5bf4f68676550434291dbdee4da52f65"; //TestRes6/EncryptFiles目录
collector2.CollectorType = YooAsset.Editor.ECollectorType.MainAssetCollector;
collector2.PackRuleName = nameof(YooAsset.Editor.PackCollector);
YooAsset.Editor.BundleCollectorSettingData.CreateCollector(archiveFileGroup, collector2);
}
}
#endif

View File

@@ -23,7 +23,7 @@ public class T1_TestEditorFileSystem : IPrebuildSetup, IPostBuildCleanup
void IPrebuildSetup.Setup()
{
#if UNITY_EDITOR
// 构建资源包
// 构建 AssetBundlePackage
{
var simulateParams = new PackageBuildParameters(TestConsts.AssetBundlePackageName);
simulateParams.BuildPipelineName = "EditorSimulateBuildPipeline";
@@ -35,7 +35,7 @@ public class T1_TestEditorFileSystem : IPrebuildSetup, IPostBuildCleanup
UnityEditor.EditorPrefs.SetString(ASSET_BUNDLE_PACKAGE_ROOT_KEY, simulateResult.PackageRootDirectory);
}
// 构建原生资源包
// 构建 RawBundlePackage
{
var simulateParams = new PackageBuildParameters(TestConsts.RawBundlePackageName);
simulateParams.BuildPipelineName = "EditorSimulateBuildPipeline";
@@ -47,7 +47,7 @@ public class T1_TestEditorFileSystem : IPrebuildSetup, IPostBuildCleanup
UnityEditor.EditorPrefs.SetString(RAW_BUNDLE_PACKAGE_ROOT_KEY, simulateResult.PackageRootDirectory);
}
// 构建归档资源包
// 构建 ArchiveBundlePackage
{
var simulateParams = new PackageBuildParameters(TestConsts.ArchiveBundlePackageName);
simulateParams.BuildPipelineName = "EditorSimulateBuildPipeline";
@@ -68,7 +68,7 @@ public class T1_TestEditorFileSystem : IPrebuildSetup, IPostBuildCleanup
[UnityTest]
public IEnumerator A_InitializePackage()
{
// 初始化资源包 ASSET_BUNDLE
// 初始化 AssetBundlePackage
{
string packageRoot = string.Empty;
#if UNITY_EDITOR
@@ -104,7 +104,7 @@ public class T1_TestEditorFileSystem : IPrebuildSetup, IPostBuildCleanup
Assert.AreEqual(EOperationStatus.Succeeded, loadPackageManifestOp.Status);
}
// 初始化资源包 RAW_BUNDLE
// 初始化 RawBundlePackage
{
string packageRoot = string.Empty;
#if UNITY_EDITOR
@@ -140,7 +140,7 @@ public class T1_TestEditorFileSystem : IPrebuildSetup, IPostBuildCleanup
Assert.AreEqual(EOperationStatus.Succeeded, loadPackageManifestOp.Status);
}
// 初始化资源包 ARCHIVE_BUNDLE
// 初始化 ArchiveBundlePackage
{
string packageRoot = string.Empty;
#if UNITY_EDITOR
@@ -248,9 +248,9 @@ public class T1_TestEditorFileSystem : IPrebuildSetup, IPostBuildCleanup
}
[UnityTest]
public IEnumerator B11_TestLoadRawFileObject()
public IEnumerator B11_TestLoadRawBundle()
{
var tester = new TestLoadRawFileObject();
var tester = new TestLoadRawBundle();
yield return tester.RuntimeTester();
}

View File

@@ -23,7 +23,7 @@ public class T2_TestBuiltinFileSystem : IPrebuildSetup, IPostBuildCleanup
void IPrebuildSetup.Setup()
{
#if UNITY_EDITOR
// 构建AssetBundlePackage
// 构建 AssetBundlePackage
{
var buildParams = new PackageBuildParameters(TestConsts.AssetBundlePackageName);
buildParams.BuildPipelineName = "ScriptableBuildPipeline";
@@ -34,7 +34,7 @@ public class T2_TestBuiltinFileSystem : IPrebuildSetup, IPostBuildCleanup
UnityEditor.EditorPrefs.SetString(ASSET_BUNDLE_PACKAGE_ROOT_KEY, simulateResult.PackageRootDirectory);
}
// 构建RawBundlePackage
// 构建 RawBundlePackage
{
var buildParams = new PackageBuildParameters(TestConsts.RawBundlePackageName);
buildParams.BuildPipelineName = "RawFileBuildPipeline";
@@ -45,7 +45,7 @@ public class T2_TestBuiltinFileSystem : IPrebuildSetup, IPostBuildCleanup
UnityEditor.EditorPrefs.SetString(RAW_BUNDLE_PACKAGE_ROOT_KEY, simulateResult.PackageRootDirectory);
}
// 构建ArchiveBundlePackage
// 构建 ArchiveBundlePackage
{
var buildParams = new PackageBuildParameters(TestConsts.ArchiveBundlePackageName);
buildParams.BuildPipelineName = "ArchiveFileBuildPipeline";
@@ -65,7 +65,7 @@ public class T2_TestBuiltinFileSystem : IPrebuildSetup, IPostBuildCleanup
[UnityTest]
public IEnumerator A_InitializePackage()
{
// 初始化资源包 ASSET_BUNDLE
// 初始化 AssetBundlePackage
{
string packageRoot = string.Empty;
#if UNITY_EDITOR
@@ -81,7 +81,7 @@ public class T2_TestBuiltinFileSystem : IPrebuildSetup, IPostBuildCleanup
var manifestServices = new TestManifestDecryptor();
initParams.BuiltinFileSystemParameters = FileSystemParameters.CreateDefaultBuiltinFileSystemParameters(packageRoot);
initParams.BuiltinFileSystemParameters.AddParameter(EFileSystemParameter.ManifestDecryptor, manifestServices);
initParams.BuiltinFileSystemParameters.AddParameter(EFileSystemParameter.AssetbundleDecryptor, new TestFileStreamDecryption());
initParams.BuiltinFileSystemParameters.AddParameter(EFileSystemParameter.AssetBundleDecryptor, new TestAssetBundleDecryptor());
var initializeOp = package.InitializePackageAsync(initParams);
yield return initializeOp;
if (initializeOp.Status != EOperationStatus.Succeeded)
@@ -104,7 +104,7 @@ public class T2_TestBuiltinFileSystem : IPrebuildSetup, IPostBuildCleanup
Assert.AreEqual(EOperationStatus.Succeeded, loadPackageManifestOp.Status);
}
// 初始化资源包 RAW_BUNDLE
// 初始化 RawBundlePackage
{
string packageRoot = string.Empty;
#if UNITY_EDITOR
@@ -118,6 +118,7 @@ public class T2_TestBuiltinFileSystem : IPrebuildSetup, IPostBuildCleanup
// 初始化资源包
var initParams = new OfflinePlayModeOptions();
initParams.BuiltinFileSystemParameters = FileSystemParameters.CreateDefaultBuiltinFileSystemParameters(packageRoot);
initParams.BuiltinFileSystemParameters.AddParameter(EFileSystemParameter.RawBundleDecryptor, new TestRawBundleDecryptor());
var initializeOp = package.InitializePackageAsync(initParams);
yield return initializeOp;
if (initializeOp.Status != EOperationStatus.Succeeded)
@@ -140,7 +141,7 @@ public class T2_TestBuiltinFileSystem : IPrebuildSetup, IPostBuildCleanup
Assert.AreEqual(EOperationStatus.Succeeded, loadPackageManifestOp.Status);
}
// 初始化资源包 ARCHIVE_BUNDLE
// 初始化 ArchiveBundlePackage
{
string packageRoot = string.Empty;
#if UNITY_EDITOR
@@ -154,6 +155,7 @@ public class T2_TestBuiltinFileSystem : IPrebuildSetup, IPostBuildCleanup
// 初始化资源包
var initParams = new OfflinePlayModeOptions();
initParams.BuiltinFileSystemParameters = FileSystemParameters.CreateDefaultBuiltinFileSystemParameters(packageRoot);
initParams.BuiltinFileSystemParameters.AddParameter(EFileSystemParameter.ArchiveBundleDecryptor, new TestArchiveBundleDecryptor());
var initializeOp = package.InitializePackageAsync(initParams);
yield return initializeOp;
if (initializeOp.Status != EOperationStatus.Succeeded)
@@ -248,9 +250,9 @@ public class T2_TestBuiltinFileSystem : IPrebuildSetup, IPostBuildCleanup
}
[UnityTest]
public IEnumerator B11_TestLoadRawFileObject()
public IEnumerator B11_TestLoadRawBundle()
{
var tester = new TestLoadRawFileObject();
var tester = new TestLoadRawBundle();
yield return tester.RuntimeTester();
}
@@ -290,9 +292,9 @@ public class T2_TestBuiltinFileSystem : IPrebuildSetup, IPostBuildCleanup
}
[UnityTest]
public IEnumerator C01_TestBundleEncryption()
public IEnumerator C01_TestAssetBundleDecryption()
{
var tester = new TestBundleEncryption();
var tester = new TestAssetBundleDecryption();
yield return tester.RuntimeTester();
}

View File

@@ -1,30 +1,32 @@
using System;
using System.Text;
using System.Collections;
using UnityEngine;
using NUnit.Framework;
using YooAsset;
/// <summary>
/// 测试加载加密文件
/// 测试加载加密的 AssetBundle 资源
/// </summary>
/// <remarks>
/// 覆盖 API: LoadAssetAsync / LoadAssetSync / InstantiateSync
/// 测试内容:
/// 1. 异步加载加密预制体prefab_encryptA验证加载成功并实例化
/// 2. 同步加载加密的预制体prefab_encryptB验证加载成功并实例化
/// 1. 使用 Memory 解密器加载加密预制体prefab_encrypt_x / prefab_encrypt_y
/// </remarks>
public class TestBundleEncryption
public class TestAssetBundleDecryption
{
public IEnumerator RuntimeTester()
{
ResourcePackage package = YooAssets.GetPackage(TestConsts.AssetBundlePackageName);
Assert.IsNotNull(package);
yield return TestEncryptedPrefabPair(package, "prefab_encrypt_x", "prefab_encrypt_y");
}
private IEnumerator TestEncryptedPrefabPair(ResourcePackage package, string asyncLocation, string syncLocation)
{
Assert.IsNotNull(package);
// 异步加载加密的预制体
// 说明:测试内置文件解压
{
var assetHandle = package.LoadAssetAsync<GameObject>("prefab_encryptA");
var assetHandle = package.LoadAssetAsync<GameObject>(asyncLocation);
yield return assetHandle;
Assert.AreEqual(EOperationStatus.Succeeded, assetHandle.Status);
@@ -36,9 +38,8 @@ public class TestBundleEncryption
}
// 同步加载加密的预制体
// 说明:测试内置文件解压
{
var assetHandle = package.LoadAssetSync<GameObject>("prefab_encryptB");
var assetHandle = package.LoadAssetSync<GameObject>(syncLocation);
Assert.AreEqual(EOperationStatus.Succeeded, assetHandle.Status);
var options = new InstantiateOptions(true, Vector3.zero, Quaternion.identity);
@@ -48,4 +49,4 @@ public class TestBundleEncryption
assetHandle.Release();
}
}
}
}

View File

@@ -34,7 +34,11 @@ public class T3_TestCacheFileSystem : IPrebuildSetup, IPostBuildCleanup
if (Directory.Exists(cacheRoot))
Directory.Delete(cacheRoot, true);
// 拷贝打包资源到本地服务器
// 清空旧的本地服务器测试目录
if (Directory.Exists(TestConsts.TestServerDirectory))
Directory.Delete(TestConsts.TestServerDirectory, true);
// 拷贝 AssetBundlePackage 到本地服务器
{
string packageRoot = string.Empty;
#if UNITY_EDITOR
@@ -46,7 +50,31 @@ public class T3_TestCacheFileSystem : IPrebuildSetup, IPostBuildCleanup
CopyDirectory(packageRoot, TestConsts.TestServerDirectory);
}
// 初始化资源包 ASSET_BUNDLE
// 拷贝 RawBundlePackage 到本地服务器
{
string packageRoot = string.Empty;
#if UNITY_EDITOR
packageRoot = UnityEditor.EditorPrefs.GetString(T2_TestBuiltinFileSystem.RAW_BUNDLE_PACKAGE_ROOT_KEY);
#endif
if (Directory.Exists(packageRoot) == false)
throw new Exception($"Not found package root : {packageRoot}");
CopyDirectory(packageRoot, TestConsts.TestServerDirectory);
}
// 拷贝 ArchiveBundlePackage 到本地服务器
{
string packageRoot = string.Empty;
#if UNITY_EDITOR
packageRoot = UnityEditor.EditorPrefs.GetString(T2_TestBuiltinFileSystem.ARCHIVE_BUNDLE_PACKAGE_ROOT_KEY);
#endif
if (Directory.Exists(packageRoot) == false)
throw new Exception($"Not found package root : {packageRoot}");
CopyDirectory(packageRoot, TestConsts.TestServerDirectory);
}
// 初始化 AssetBundlePackage
{
var package = YooAssets.CreatePackage(TestConsts.AssetBundlePackageName);
@@ -58,7 +86,71 @@ public class T3_TestCacheFileSystem : IPrebuildSetup, IPostBuildCleanup
initParams.BuiltinFileSystemParameters = null;
initParams.CacheFileSystemParameters = FileSystemParameters.CreateDefaultSandboxFileSystemParameters(remoteService);
initParams.CacheFileSystemParameters.AddParameter(EFileSystemParameter.ManifestDecryptor, manifestServices);
initParams.CacheFileSystemParameters.AddParameter(EFileSystemParameter.AssetbundleDecryptor, new TestFileStreamDecryption());
initParams.CacheFileSystemParameters.AddParameter(EFileSystemParameter.AssetBundleDecryptor, new TestAssetBundleDecryptor());
var initializeOp = package.InitializePackageAsync(initParams);
yield return initializeOp;
if (initializeOp.Status != EOperationStatus.Succeeded)
Debug.LogError(initializeOp.Error);
Assert.AreEqual(EOperationStatus.Succeeded, initializeOp.Status);
// 请求资源版本
var requestVersionOp = package.RequestPackageVersionAsync();
yield return requestVersionOp;
if (requestVersionOp.Status != EOperationStatus.Succeeded)
Debug.LogError(requestVersionOp.Error);
Assert.AreEqual(EOperationStatus.Succeeded, requestVersionOp.Status);
// 更新资源清单
var loadPackageManifestOptions = new LoadPackageManifestOptions(requestVersionOp.PackageVersion, 60);
var loadPackageManifestOp = package.LoadPackageManifestAsync(loadPackageManifestOptions);
yield return loadPackageManifestOp;
if (loadPackageManifestOp.Status != EOperationStatus.Succeeded)
Debug.LogError(loadPackageManifestOp.Error);
Assert.AreEqual(EOperationStatus.Succeeded, loadPackageManifestOp.Status);
}
// 初始化 RawBundlePackage
{
var package = YooAssets.CreatePackage(TestConsts.RawBundlePackageName);
// 初始化资源包
var initParams = new HostPlayModeOptions();
var remoteService = new TestRemoteService(TestConsts.TestServerURL);
initParams.BuiltinFileSystemParameters = null;
initParams.CacheFileSystemParameters = FileSystemParameters.CreateDefaultSandboxFileSystemParameters(remoteService);
initParams.CacheFileSystemParameters.AddParameter(EFileSystemParameter.RawBundleDecryptor, new TestRawBundleDecryptor());
var initializeOp = package.InitializePackageAsync(initParams);
yield return initializeOp;
if (initializeOp.Status != EOperationStatus.Succeeded)
Debug.LogError(initializeOp.Error);
Assert.AreEqual(EOperationStatus.Succeeded, initializeOp.Status);
// 请求资源版本
var requestVersionOp = package.RequestPackageVersionAsync();
yield return requestVersionOp;
if (requestVersionOp.Status != EOperationStatus.Succeeded)
Debug.LogError(requestVersionOp.Error);
Assert.AreEqual(EOperationStatus.Succeeded, requestVersionOp.Status);
// 更新资源清单
var loadPackageManifestOptions = new LoadPackageManifestOptions(requestVersionOp.PackageVersion, 60);
var loadPackageManifestOp = package.LoadPackageManifestAsync(loadPackageManifestOptions);
yield return loadPackageManifestOp;
if (loadPackageManifestOp.Status != EOperationStatus.Succeeded)
Debug.LogError(loadPackageManifestOp.Error);
Assert.AreEqual(EOperationStatus.Succeeded, loadPackageManifestOp.Status);
}
// 初始化 ArchiveBundlePackage
{
var package = YooAssets.CreatePackage(TestConsts.ArchiveBundlePackageName);
// 初始化资源包
var initParams = new HostPlayModeOptions();
var remoteService = new TestRemoteService(TestConsts.TestServerURL);
initParams.BuiltinFileSystemParameters = null;
initParams.CacheFileSystemParameters = FileSystemParameters.CreateDefaultSandboxFileSystemParameters(remoteService);
initParams.CacheFileSystemParameters.AddParameter(EFileSystemParameter.ArchiveBundleDecryptor, new TestArchiveBundleDecryptor());
var initializeOp = package.InitializePackageAsync(initParams);
yield return initializeOp;
if (initializeOp.Status != EOperationStatus.Succeeded)
@@ -120,14 +212,182 @@ public class T3_TestCacheFileSystem : IPrebuildSetup, IPostBuildCleanup
}
[UnityTest]
public IEnumerator C04_TestClearCache()
public IEnumerator D01_TestAsyncTask()
{
var tester = new TestAsyncTask();
yield return tester.RuntimeTester();
}
[UnityTest]
public IEnumerator D02_TestAsyncCompleted()
{
var tester = new TestAsyncCompleted();
yield return tester.RuntimeTester();
}
[UnityTest]
public IEnumerator D03_TestLoadAsset()
{
var tester = new TestLoadAsset();
yield return tester.RuntimeTester();
}
[UnityTest]
public IEnumerator D04_TestLoadSubAssets()
{
var tester = new TestLoadSubAssets();
yield return tester.RuntimeTester();
}
[UnityTest]
public IEnumerator D05_TestLoadAllAssets()
{
var tester = new TestLoadAllAssets();
yield return tester.RuntimeTester();
}
[UnityTest]
public IEnumerator D06_TestLoadGameObject()
{
var tester = new TestLoadGameObject();
yield return tester.RuntimeTester();
}
[UnityTest]
public IEnumerator D07_TestLoadSpriteAtlas()
{
var tester = new TestLoadSpriteAtlas();
yield return tester.RuntimeTester();
}
[UnityTest]
public IEnumerator D08_TestLoadScriptableObject()
{
var tester = new TestLoadScriptableObject();
yield return tester.RuntimeTester();
}
[UnityTest]
public IEnumerator D09_TestLoadScene()
{
var tester = new TestLoadScene();
yield return tester.RuntimeTester();
}
[UnityTest]
public IEnumerator D10_TestLoadBundleFile()
{
var tester = new TestLoadBundleFile();
yield return tester.RuntimeTester();
}
[UnityTest]
public IEnumerator D11_TestLoadRawBundle()
{
var tester = new TestLoadRawBundle();
yield return tester.RuntimeTester();
}
[UnityTest]
public IEnumerator D12_TestLoadArchiveBundle()
{
var tester = new TestLoadArchiveBundle();
yield return tester.RuntimeTester();
}
[UnityTest]
public IEnumerator D13_TestEnsureBundleFile_RawBundle()
{
var tester = new TestEnsureBundleFile();
yield return tester.RuntimeTester_RawBundle();
}
[UnityTest]
public IEnumerator D14_TestEnsureBundleFile_AssetBundle()
{
var tester = new TestEnsureBundleFile();
yield return tester.RuntimeTester_AssetBundle();
}
[UnityTest]
public IEnumerator D15_TestEnsureBundleFile_ArchiveBundle()
{
var tester = new TestEnsureBundleFile();
yield return tester.RuntimeTester_ArchiveBundle();
}
[UnityTest]
public IEnumerator D16_TestUniTask()
{
var tester = new TestUniTask();
yield return tester.RuntimeTester();
}
[UnityTest]
public IEnumerator E01_TestGetPackageInfo()
{
var tester = new TestGetPackageInfo();
yield return tester.RuntimeTester();
}
[UnityTest]
public IEnumerator E02_TestGetAssetInfo()
{
var tester = new TestGetAssetInfo();
yield return tester.RuntimeTester();
}
[UnityTest]
public IEnumerator E03_TestIsLocationValid()
{
var tester = new TestIsLocationValid();
yield return tester.RuntimeTester();
}
[UnityTest]
public IEnumerator E04_TestLoadInvalidAsset()
{
var tester = new TestLoadInvalidAsset();
yield return tester.RuntimeTester();
}
[UnityTest]
public IEnumerator E05_TestDuplicateLoad()
{
var tester = new TestDuplicateLoad();
yield return tester.RuntimeTester();
}
[UnityTest]
public IEnumerator E06_TestHandleRelease()
{
var tester = new TestHandleRelease();
yield return tester.RuntimeTester();
}
[UnityTest]
public IEnumerator E07_TestBundleFileRelease()
{
var tester = new TestBundleFileRelease();
yield return tester.RuntimeTester();
}
[UnityTest]
public IEnumerator E08_TestUnloadAllAssets()
{
var tester = new TestUnloadAllAssets();
yield return tester.RuntimeTester();
}
[UnityTest]
public IEnumerator F01_TestClearCache()
{
var tester = new TestClearCache();
yield return tester.RuntimeTester();
}
[UnityTest]
public IEnumerator C05_TestClearManifest()
public IEnumerator F02_TestClearManifest()
{
var tester = new TestClearManifest();
yield return tester.RuntimeTester();
@@ -137,7 +397,7 @@ public class T3_TestCacheFileSystem : IPrebuildSetup, IPostBuildCleanup
public IEnumerator Z_DestroyPackage()
{
var tester = new TestDestroyPackage();
yield return tester.RuntimeTester(false);
yield return tester.RuntimeTester(true, true);
}
private static void CopyDirectory(string sourceDir, string targetDir)

View File

@@ -12,8 +12,8 @@ using YooAsset;
/// 覆盖 API: GetDownloadSize / LoadAssetAsync / LoadAssetSync / UnloadUnusedAssetsAsync
/// 测试内容:
/// 1. 验证目标远端资源的下载大小非零(尚未缓存)
/// 2. 异步加载远端资源prefab_encryptA),验证首次加载触发下载并最终成功
/// 3. 同步加载远端资源prefab_encryptB),首次应失败并触发后台下载
/// 2. 异步加载远端资源prefab_encrypt_x),验证首次加载触发下载并最终成功
/// 3. 同步加载远端资源prefab_encrypt_y),首次应失败并触发后台下载
/// 4. 释放失败的 Handle 并清理资源,等待下载完成后再次同步加载,验证成功
/// </remarks>
public class TestBundlePlaying
@@ -23,18 +23,18 @@ public class TestBundlePlaying
ResourcePackage package = YooAssets.GetPackage(TestConsts.AssetBundlePackageName);
Assert.IsNotNull(package);
if (package.GetDownloadSize("prefab_encryptA") == 0)
if (package.GetDownloadSize("prefab_encrypt_x") == 0)
{
Assert.Fail("Load bundle is already existed !");
}
if (package.GetDownloadSize("prefab_encryptB") == 0)
if (package.GetDownloadSize("prefab_encrypt_y") == 0)
{
Assert.Fail("Load bundle is already existed !");
}
// 测试异步加载远端资源
{
var assetsHandle = package.LoadAssetAsync<GameObject>("prefab_encryptA");
var assetsHandle = package.LoadAssetAsync<GameObject>("prefab_encrypt_x");
yield return assetsHandle;
Assert.AreEqual(EOperationStatus.Succeeded, assetsHandle.Status);
assetsHandle.Release();
@@ -45,7 +45,7 @@ public class TestBundlePlaying
{
// 验证失败结果
UnityEngine.TestTools.LogAssert.ignoreFailingMessages = true;
var assetsHandle = package.LoadAssetSync<GameObject>("prefab_encryptB");
var assetsHandle = package.LoadAssetSync<GameObject>("prefab_encrypt_y");
UnityEngine.TestTools.LogAssert.ignoreFailingMessages = false;
Assert.AreEqual(EOperationStatus.Failed, assetsHandle.Status);
@@ -57,7 +57,7 @@ public class TestBundlePlaying
// 验证成功结果
yield return new WaitForSeconds(1f);
UnityEngine.TestTools.LogAssert.ignoreFailingMessages = true;
assetsHandle = package.LoadAssetSync<GameObject>("prefab_encryptB");
assetsHandle = package.LoadAssetSync<GameObject>("prefab_encrypt_y");
UnityEngine.TestTools.LogAssert.ignoreFailingMessages = false;
Assert.AreEqual(EOperationStatus.Succeeded, assetsHandle.Status);
assetsHandle.Release();

View File

@@ -30,12 +30,12 @@ public class TestClearCache
// ---- 1. ClearBundleFilesByLocations ----
{
var options = new ClearCacheOptions(ClearCacheMethods.ClearBundleFilesByLocations, "prefab_encryptA");
var options = new ClearCacheOptions(ClearCacheMethods.ClearBundleFilesByLocations, "prefab_encrypt_x");
var clearOp = package.ClearCacheAsync(options);
yield return clearOp;
Assert.AreEqual(EOperationStatus.Succeeded, clearOp.Status);
Assert.Greater(package.GetDownloadSize("prefab_encryptA"), 0);
Assert.Greater(package.GetDownloadSize("prefab_encrypt_x"), 0);
}
// ---- 2. ClearBundleFilesByTags ----
@@ -53,7 +53,7 @@ public class TestClearCache
yield return clearOp;
Assert.AreEqual(EOperationStatus.Succeeded, clearOp.Status);
Assert.Greater(package.GetDownloadSize("prefab_encryptA"), 0);
Assert.Greater(package.GetDownloadSize("prefab_encrypt_x"), 0);
}
// ---- 4. ClearAllBundleFiles ----

View File

@@ -12,17 +12,24 @@ using YooAsset;
/// 测试内容:
/// 1. 创建资源下载器,验证需要下载的资源数量非零
/// 2. 启动下载并等待完成,验证下载状态为成功
/// 3. 覆盖 AssetBundle、RawBundle 和 ArchiveBundle 三个包裹
/// </remarks>
public class TestResourceDownloader
{
public IEnumerator RuntimeTester()
{
ResourcePackage package = YooAssets.GetPackage(TestConsts.AssetBundlePackageName);
yield return DownloadPackage(TestConsts.AssetBundlePackageName);
yield return DownloadPackage(TestConsts.RawBundlePackageName);
yield return DownloadPackage(TestConsts.ArchiveBundlePackageName);
}
private IEnumerator DownloadPackage(string packageName)
{
ResourcePackage package = YooAssets.GetPackage(packageName);
Assert.IsNotNull(package);
var options = new ResourceDownloaderOptions(10, 1);
var downloader = package.CreateResourceDownloader(options);
Assert.AreNotEqual(0, downloader.TotalDownloadCount);
Assert.AreNotEqual(0, downloader.TotalDownloadCount, $"{packageName} no files need to download.");
downloader.StartDownload();
yield return downloader;

View File

@@ -29,13 +29,13 @@ public class TestResourceImporter
string fileRoot = $"{packageDir.Parent.FullName}/OutputCache";
var fileInfoA = new ImportBundleInfo(
filePath: $"{fileRoot}/assets_samples_test_sample_testres3_import_prefab_importa.bundle.encrypt",
bundleName: "assets_samples_test_sample_testres3_import_prefab_importa.bundle",
filePath: $"{fileRoot}/assets_samples_test_sample_testres3_importfiles_prefab_import_a.bundle.encrypt",
bundleName: "assets_samples_test_sample_testres3_importfiles_prefab_import_a.bundle",
bundleGuid: null);
var fileInfoB = new ImportBundleInfo(
filePath: $"{fileRoot}/assets_samples_test_sample_testres3_import_prefab_importb.bundle.encrypt",
bundleName: "assets_samples_test_sample_testres3_import_prefab_importb.bundle",
filePath: $"{fileRoot}/assets_samples_test_sample_testres3_importfiles_prefab_import_b.bundle.encrypt",
bundleName: "assets_samples_test_sample_testres3_importfiles_prefab_import_b.bundle",
bundleGuid: null);
ImportBundleInfo[] importInfos = { fileInfoA, fileInfoB };

View File

@@ -10,7 +10,7 @@ GameObject:
m_Component:
- component: {fileID: 1740794871874781600}
m_Layer: 0
m_Name: prefab_encryptA
m_Name: prefab_encrypt_x
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0

View File

@@ -10,7 +10,7 @@ GameObject:
m_Component:
- component: {fileID: 1740794871874781600}
m_Layer: 0
m_Name: prefab_encryptB
m_Name: prefab_encrypt_y
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0

View File

@@ -46,9 +46,9 @@ SpriteAtlas:
- {fileID: 21300000, guid: fcab35236e33438448805a9211b0cc19, type: 3}
- {fileID: 21300000, guid: 3b8db7241fc8ff54e9dea6fc64cfd7e5, type: 3}
m_PackedSpriteNamesToIndex:
- pause
- bullet
- rocket
- sprite_b
- sprite_a
- sprite_c
m_RenderDataMap: {}
m_Tag: atlas_icon
m_IsVariant: 0

View File

Before

Width:  |  Height:  |  Size: 111 KiB

After

Width:  |  Height:  |  Size: 111 KiB

View File

Before

Width:  |  Height:  |  Size: 104 KiB

After

Width:  |  Height:  |  Size: 104 KiB

View File

Before

Width:  |  Height:  |  Size: 106 KiB

After

Width:  |  Height:  |  Size: 106 KiB

View File

@@ -10,7 +10,7 @@ GameObject:
m_Component:
- component: {fileID: 7029742931587134142}
m_Layer: 0
m_Name: prefab_unpackA
m_Name: prefab_import_a
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0

View File

@@ -10,7 +10,7 @@ GameObject:
m_Component:
- component: {fileID: 7029742931587134142}
m_Layer: 0
m_Name: prefab_unpackB
m_Name: prefab_import_b
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0

View File

@@ -10,7 +10,7 @@ GameObject:
m_Component:
- component: {fileID: 7029742931587134142}
m_Layer: 0
m_Name: prefab_importA
m_Name: prefab_unpack_a
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0

View File

@@ -10,7 +10,7 @@ GameObject:
m_Component:
- component: {fileID: 7029742931587134142}
m_Layer: 0
m_Name: prefab_importB
m_Name: prefab_unpack_b
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0

View File

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

View File

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

View File

@@ -0,0 +1 @@
this is raw file x !

View File

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

View File

@@ -0,0 +1 @@
this is raw file y !

View File

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

View File

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

View File

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

Some files were not shown because too many files have changed in this diff Show More