//------------------------------------------------------------
// Game Framework
// Copyright © 2013-2021 loyalsoft. All rights reserved.
// Homepage: http://www.game7000.com/
// Feedback: http://www.game7000.com/
//------------------------------------------------------------
using GameFramework.Download;
using GameFramework.FileSystem;
using System;
using System.Collections.Generic;
using System.IO;
namespace GameFramework.Resource
{
internal sealed partial class ResourceManager : GameFrameworkModule, IResourceManager
{
///
/// 资源更新器。
///
private sealed partial class ResourceUpdater
{
private const int CachedHashBytesLength = 4;
private const int CachedBytesLength = 0x1000;
private readonly ResourceManager m_ResourceManager;
private readonly List m_ApplyWaitingInfo;
private readonly List m_UpdateWaitingInfo;
private readonly HashSet m_UpdateWaitingInfoWhilePlaying;
private readonly Dictionary m_UpdateCandidateInfo;
private readonly SortedDictionary> m_CachedFileSystemsForGenerateReadWriteVersionList;
private readonly List m_CachedResourceNames;
private readonly byte[] m_CachedHashBytes;
private readonly byte[] m_CachedBytes;
private IDownloadManager m_DownloadManager;
private bool m_CheckResourcesComplete;
private string m_ApplyingResourcePackPath;
private FileStream m_ApplyingResourcePackStream;
private ResourceGroup m_UpdatingResourceGroup;
private int m_GenerateReadWriteVersionListLength;
private int m_CurrentGenerateReadWriteVersionListLength;
private int m_UpdateRetryCount;
private bool m_FailureFlag;
private string m_ReadWriteVersionListFileName;
private string m_ReadWriteVersionListTempFileName;
public GameFrameworkAction ResourceApplySuccess;
public GameFrameworkAction ResourceApplyFailure;
public GameFrameworkAction ResourceApplyComplete;
public GameFrameworkAction ResourceUpdateStart;
public GameFrameworkAction ResourceUpdateChanged;
public GameFrameworkAction ResourceUpdateSuccess;
public GameFrameworkAction ResourceUpdateFailure;
public GameFrameworkAction ResourceUpdateComplete;
public GameFrameworkAction ResourceUpdateAllComplete;
///
/// 初始化资源更新器的新实例。
///
/// 资源管理器。
public ResourceUpdater(ResourceManager resourceManager)
{
m_ResourceManager = resourceManager;
m_ApplyWaitingInfo = new List();
m_UpdateWaitingInfo = new List();
m_UpdateWaitingInfoWhilePlaying = new HashSet();
m_UpdateCandidateInfo = new Dictionary();
m_CachedFileSystemsForGenerateReadWriteVersionList = new SortedDictionary>(StringComparer.Ordinal);
m_CachedResourceNames = new List();
m_CachedHashBytes = new byte[CachedHashBytesLength];
m_CachedBytes = new byte[CachedBytesLength];
m_DownloadManager = null;
m_CheckResourcesComplete = false;
m_ApplyingResourcePackPath = null;
m_ApplyingResourcePackStream = null;
m_UpdatingResourceGroup = null;
m_GenerateReadWriteVersionListLength = 0;
m_CurrentGenerateReadWriteVersionListLength = 0;
m_UpdateRetryCount = 3;
m_FailureFlag = false;
m_ReadWriteVersionListFileName = Utility.Path.GetRegularPath(Path.Combine(m_ResourceManager.m_ReadWritePath, LocalVersionListFileName));
m_ReadWriteVersionListTempFileName = Utility.Text.Format("{0}.{1}", m_ReadWriteVersionListFileName, TempExtension);
ResourceApplySuccess = null;
ResourceApplyFailure = null;
ResourceApplyComplete = null;
ResourceUpdateStart = null;
ResourceUpdateChanged = null;
ResourceUpdateSuccess = null;
ResourceUpdateFailure = null;
ResourceUpdateComplete = null;
ResourceUpdateAllComplete = null;
}
///
/// 获取或设置每更新多少字节的资源,重新生成一次版本资源列表。
///
public int GenerateReadWriteVersionListLength
{
get
{
return m_GenerateReadWriteVersionListLength;
}
set
{
m_GenerateReadWriteVersionListLength = value;
}
}
///
/// 获取正在应用的资源包路径。
///
public string ApplyingResourcePackPath
{
get
{
return m_ApplyingResourcePackPath;
}
}
///
/// 获取等待应用资源数量。
///
public int ApplyWaitingCount
{
get
{
return m_ApplyWaitingInfo.Count;
}
}
///
/// 获取或设置资源更新重试次数。
///
public int UpdateRetryCount
{
get
{
return m_UpdateRetryCount;
}
set
{
m_UpdateRetryCount = value;
}
}
///
/// 获取正在更新的资源组。
///
public IResourceGroup UpdatingResourceGroup
{
get
{
return m_UpdatingResourceGroup;
}
}
///
/// 获取等待更新资源数量。
///
public int UpdateWaitingCount
{
get
{
return m_UpdateWaitingInfo.Count;
}
}
///
/// 获取使用时下载的等待更新资源数量。
///
public int UpdateWaitingWhilePlayingCount
{
get
{
return m_UpdateWaitingInfoWhilePlaying.Count;
}
}
///
/// 获取候选更新资源数量。
///
public int UpdateCandidateCount
{
get
{
return m_UpdateCandidateInfo.Count;
}
}
///
/// 资源更新器轮询。
///
/// 逻辑流逝时间,以秒为单位。
/// 真实流逝时间,以秒为单位。
public void Update(float elapseSeconds, float realElapseSeconds)
{
if (m_ApplyingResourcePackStream != null)
{
while (m_ApplyWaitingInfo.Count > 0)
{
ApplyInfo applyInfo = m_ApplyWaitingInfo[0];
m_ApplyWaitingInfo.RemoveAt(0);
if (ApplyResource(applyInfo))
{
return;
}
}
Array.Clear(m_CachedBytes, 0, CachedBytesLength);
string resourcePackPath = m_ApplyingResourcePackPath;
m_ApplyingResourcePackPath = null;
m_ApplyingResourcePackStream.Dispose();
m_ApplyingResourcePackStream = null;
if (ResourceApplyComplete != null)
{
ResourceApplyComplete(resourcePackPath, !m_FailureFlag);
}
if (m_UpdateCandidateInfo.Count <= 0 && ResourceUpdateAllComplete != null)
{
ResourceUpdateAllComplete();
}
return;
}
if (m_UpdateWaitingInfo.Count > 0)
{
int freeCount = m_DownloadManager.FreeAgentCount - m_DownloadManager.WaitingTaskCount;
if (freeCount > 0)
{
for (int i = 0, count = 0; i < m_UpdateWaitingInfo.Count && count < freeCount; i++)
{
if (DownloadResource(m_UpdateWaitingInfo[i]))
{
count++;
}
}
}
return;
}
}
///
/// 关闭并清理资源更新器。
///
public void Shutdown()
{
if (m_DownloadManager != null)
{
m_DownloadManager.DownloadStart -= OnDownloadStart;
m_DownloadManager.DownloadUpdate -= OnDownloadUpdate;
m_DownloadManager.DownloadSuccess -= OnDownloadSuccess;
m_DownloadManager.DownloadFailure -= OnDownloadFailure;
}
m_UpdateWaitingInfo.Clear();
m_UpdateCandidateInfo.Clear();
m_CachedFileSystemsForGenerateReadWriteVersionList.Clear();
}
///
/// 设置下载管理器。
///
/// 下载管理器。
public void SetDownloadManager(IDownloadManager downloadManager)
{
if (downloadManager == null)
{
throw new GameFrameworkException("Download manager is invalid.");
}
m_DownloadManager = downloadManager;
m_DownloadManager.DownloadStart += OnDownloadStart;
m_DownloadManager.DownloadUpdate += OnDownloadUpdate;
m_DownloadManager.DownloadSuccess += OnDownloadSuccess;
m_DownloadManager.DownloadFailure += OnDownloadFailure;
}
///
/// 增加资源更新。
///
/// 资源名称。
/// 资源所在的文件系统名称。
/// 资源加载方式。
/// 资源大小。
/// 资源哈希值。
/// 压缩后大小。
/// 压缩后哈希值。
/// 资源路径。
public void AddResourceUpdate(ResourceName resourceName, string fileSystemName, LoadType loadType, int length, int hashCode, int compressedLength, int compressedHashCode, string resourcePath)
{
m_UpdateCandidateInfo.Add(resourceName, new UpdateInfo(resourceName, fileSystemName, loadType, length, hashCode, compressedLength, compressedHashCode, resourcePath));
}
///
/// 检查资源完成。
///
/// 是否需要生成读写区版本资源列表。
public void CheckResourceComplete(bool needGenerateReadWriteVersionList)
{
m_CheckResourcesComplete = true;
if (needGenerateReadWriteVersionList)
{
GenerateReadWriteVersionList();
}
}
///
/// 应用指定资源包的资源。
///
/// 要应用的资源包路径。
public void ApplyResources(string resourcePackPath)
{
if (!m_CheckResourcesComplete)
{
throw new GameFrameworkException("You must check resources complete first.");
}
if (m_ApplyingResourcePackStream != null)
{
throw new GameFrameworkException(Utility.Text.Format("There is already a resource pack '{0}' being applied.", m_ApplyingResourcePackPath));
}
if (m_UpdatingResourceGroup != null)
{
throw new GameFrameworkException(Utility.Text.Format("There is already a resource group '{0}' being updated.", m_UpdatingResourceGroup.Name));
}
if (m_UpdateWaitingInfoWhilePlaying.Count > 0)
{
throw new GameFrameworkException("There are already some resources being updated while playing.");
}
try
{
long length = 0L;
ResourcePackVersionList versionList = default(ResourcePackVersionList);
using (FileStream fileStream = new FileStream(resourcePackPath, FileMode.Open, FileAccess.Read))
{
length = fileStream.Length;
versionList = m_ResourceManager.m_ResourcePackVersionListSerializer.Deserialize(fileStream);
}
if (!versionList.IsValid)
{
throw new GameFrameworkException("Deserialize resource pack version list failure.");
}
if (versionList.Offset + versionList.Length != length)
{
throw new GameFrameworkException("Resource pack length is invalid.");
}
m_ApplyingResourcePackPath = resourcePackPath;
m_ApplyingResourcePackStream = new FileStream(resourcePackPath, FileMode.Open, FileAccess.Read);
m_ApplyingResourcePackStream.Position = versionList.Offset;
m_FailureFlag = false;
ResourcePackVersionList.Resource[] resources = versionList.GetResources();
foreach (ResourcePackVersionList.Resource resource in resources)
{
ResourceName resourceName = new ResourceName(resource.Name, resource.Variant, resource.Extension);
UpdateInfo updateInfo = null;
if (!m_UpdateCandidateInfo.TryGetValue(resourceName, out updateInfo))
{
continue;
}
if (updateInfo.LoadType == (LoadType)resource.LoadType && updateInfo.Length == resource.Length && updateInfo.HashCode == resource.HashCode)
{
m_ApplyWaitingInfo.Add(new ApplyInfo(resourceName, updateInfo.FileSystemName, (LoadType)resource.LoadType, resource.Offset, resource.Length, resource.HashCode, resource.CompressedLength, resource.CompressedHashCode, updateInfo.ResourcePath));
}
}
}
catch (Exception exception)
{
if (m_ApplyingResourcePackStream != null)
{
m_ApplyingResourcePackStream.Dispose();
m_ApplyingResourcePackStream = null;
}
throw new GameFrameworkException(Utility.Text.Format("Apply resources '{0}' with exception '{1}'.", resourcePackPath, exception), exception);
}
}
///
/// 更新指定资源组的资源。
///
/// 要更新的资源组。
public void UpdateResources(ResourceGroup resourceGroup)
{
if (m_DownloadManager == null)
{
throw new GameFrameworkException("You must set download manager first.");
}
if (!m_CheckResourcesComplete)
{
throw new GameFrameworkException("You must check resources complete first.");
}
if (m_ApplyingResourcePackStream != null)
{
throw new GameFrameworkException(Utility.Text.Format("There is already a resource pack '{0}' being applied.", m_ApplyingResourcePackPath));
}
if (m_UpdatingResourceGroup != null)
{
throw new GameFrameworkException(Utility.Text.Format("There is already a resource group '{0}' being updated.", m_UpdatingResourceGroup.Name));
}
if (string.IsNullOrEmpty(resourceGroup.Name))
{
foreach (KeyValuePair updateInfo in m_UpdateCandidateInfo)
{
m_UpdateWaitingInfo.Add(updateInfo.Value);
}
}
else
{
resourceGroup.InternalGetResourceNames(m_CachedResourceNames);
foreach (ResourceName resourceName in m_CachedResourceNames)
{
UpdateInfo updateInfo = null;
if (!m_UpdateCandidateInfo.TryGetValue(resourceName, out updateInfo))
{
continue;
}
m_UpdateWaitingInfo.Add(updateInfo);
}
m_CachedResourceNames.Clear();
}
m_UpdatingResourceGroup = resourceGroup;
m_FailureFlag = false;
}
///
/// 停止更新资源。
///
public void StopUpdateResources()
{
if (m_DownloadManager == null)
{
throw new GameFrameworkException("You must set download manager first.");
}
if (!m_CheckResourcesComplete)
{
throw new GameFrameworkException("You must check resources complete first.");
}
if (m_ApplyingResourcePackStream != null)
{
throw new GameFrameworkException(Utility.Text.Format("There is already a resource pack '{0}' being applied.", m_ApplyingResourcePackPath));
}
if (m_UpdatingResourceGroup == null)
{
throw new GameFrameworkException("There is no resource group being updated.");
}
m_UpdateWaitingInfo.Clear();
m_UpdatingResourceGroup = null;
}
///
/// 更新指定资源。
///
/// 要更新的资源名称。
public void UpdateResource(ResourceName resourceName)
{
if (m_DownloadManager == null)
{
throw new GameFrameworkException("You must set download manager first.");
}
if (!m_CheckResourcesComplete)
{
throw new GameFrameworkException("You must check resources complete first.");
}
if (m_ApplyingResourcePackStream != null)
{
throw new GameFrameworkException(Utility.Text.Format("There is already a resource pack '{0}' being applied.", m_ApplyingResourcePackPath));
}
UpdateInfo updateInfo = null;
if (m_UpdateCandidateInfo.TryGetValue(resourceName, out updateInfo) && m_UpdateWaitingInfoWhilePlaying.Add(updateInfo))
{
DownloadResource(updateInfo);
}
}
private bool ApplyResource(ApplyInfo applyInfo)
{
long position = m_ApplyingResourcePackStream.Position;
try
{
bool compressed = applyInfo.Length != applyInfo.CompressedLength || applyInfo.HashCode != applyInfo.CompressedHashCode;
int bytesRead = 0;
int bytesLeft = applyInfo.CompressedLength;
string directory = Path.GetDirectoryName(applyInfo.ResourcePath);
if (!Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}
m_ApplyingResourcePackStream.Position += applyInfo.Offset;
using (FileStream fileStream = new FileStream(applyInfo.ResourcePath, FileMode.Create, FileAccess.ReadWrite))
{
while ((bytesRead = m_ApplyingResourcePackStream.Read(m_CachedBytes, 0, bytesLeft < CachedBytesLength ? bytesLeft : CachedBytesLength)) > 0)
{
bytesLeft -= bytesRead;
fileStream.Write(m_CachedBytes, 0, bytesRead);
}
if (compressed)
{
fileStream.Position = 0L;
int hashCode = Utility.Verifier.GetCrc32(fileStream);
if (hashCode != applyInfo.CompressedHashCode)
{
if (ResourceApplyFailure != null)
{
string errorMessage = Utility.Text.Format("Resource compressed hash code error, need '{0}', applied '{1}'.", applyInfo.CompressedHashCode, hashCode);
ResourceApplyFailure(applyInfo.ResourceName, m_ApplyingResourcePackPath, errorMessage);
}
m_FailureFlag = true;
return false;
}
if (m_ResourceManager.m_DecompressCachedStream == null)
{
m_ResourceManager.m_DecompressCachedStream = new MemoryStream();
}
fileStream.Position = 0L;
m_ResourceManager.m_DecompressCachedStream.Position = 0L;
m_ResourceManager.m_DecompressCachedStream.SetLength(0L);
if (!Utility.Compression.Decompress(fileStream, m_ResourceManager.m_DecompressCachedStream))
{
if (ResourceApplyFailure != null)
{
string errorMessage = Utility.Text.Format("Unable to decompress resource '{0}'.", applyInfo.ResourcePath);
ResourceApplyFailure(applyInfo.ResourceName, m_ApplyingResourcePackPath, errorMessage);
}
m_FailureFlag = true;
return false;
}
fileStream.Position = 0L;
fileStream.SetLength(0L);
fileStream.Write(m_ResourceManager.m_DecompressCachedStream.GetBuffer(), 0, (int)m_ResourceManager.m_DecompressCachedStream.Length);
}
else
{
int hashCode = 0;
fileStream.Position = 0L;
if (applyInfo.LoadType == LoadType.LoadFromMemoryAndQuickDecrypt || applyInfo.LoadType == LoadType.LoadFromMemoryAndDecrypt
|| applyInfo.LoadType == LoadType.LoadFromBinaryAndQuickDecrypt || applyInfo.LoadType == LoadType.LoadFromBinaryAndDecrypt)
{
Utility.Converter.GetBytes(applyInfo.HashCode, m_CachedHashBytes);
if (applyInfo.LoadType == LoadType.LoadFromMemoryAndQuickDecrypt || applyInfo.LoadType == LoadType.LoadFromBinaryAndQuickDecrypt)
{
hashCode = Utility.Verifier.GetCrc32(fileStream, m_CachedHashBytes, Utility.Encryption.QuickEncryptLength);
}
else if (applyInfo.LoadType == LoadType.LoadFromMemoryAndDecrypt || applyInfo.LoadType == LoadType.LoadFromBinaryAndDecrypt)
{
hashCode = Utility.Verifier.GetCrc32(fileStream, m_CachedHashBytes, applyInfo.Length);
}
Array.Clear(m_CachedHashBytes, 0, CachedHashBytesLength);
}
else
{
hashCode = Utility.Verifier.GetCrc32(fileStream);
}
if (hashCode != applyInfo.HashCode)
{
if (ResourceApplyFailure != null)
{
string errorMessage = Utility.Text.Format("Resource hash code error, need '{0}', applied '{1}'.", applyInfo.HashCode, hashCode);
ResourceApplyFailure(applyInfo.ResourceName, m_ApplyingResourcePackPath, errorMessage);
}
m_FailureFlag = true;
return false;
}
}
}
if (applyInfo.UseFileSystem)
{
IFileSystem fileSystem = m_ResourceManager.GetFileSystem(applyInfo.FileSystemName, false);
bool retVal = fileSystem.WriteFile(applyInfo.ResourceName.FullName, applyInfo.ResourcePath);
if (File.Exists(applyInfo.ResourcePath))
{
File.Delete(applyInfo.ResourcePath);
}
if (!retVal)
{
if (ResourceApplyFailure != null)
{
string errorMessage = Utility.Text.Format("Unable to write resource '{0}' to file system '{1}'.", applyInfo.ResourcePath, applyInfo.FileSystemName);
ResourceApplyFailure(applyInfo.ResourceName, m_ApplyingResourcePackPath, errorMessage);
}
m_FailureFlag = true;
return false;
}
}
string downloadingResource = Utility.Text.Format("{0}.download", applyInfo.ResourcePath);
if (File.Exists(downloadingResource))
{
File.Delete(downloadingResource);
}
m_UpdateCandidateInfo.Remove(applyInfo.ResourceName);
m_ResourceManager.m_ResourceInfos[applyInfo.ResourceName].MarkReady();
m_ResourceManager.m_ReadWriteResourceInfos.Add(applyInfo.ResourceName, new ReadWriteResourceInfo(applyInfo.FileSystemName, applyInfo.LoadType, applyInfo.Length, applyInfo.HashCode));
if (ResourceApplySuccess != null)
{
ResourceApplySuccess(applyInfo.ResourceName, applyInfo.ResourcePath, m_ApplyingResourcePackPath, applyInfo.Length, applyInfo.CompressedLength);
}
m_CurrentGenerateReadWriteVersionListLength += applyInfo.CompressedLength;
if (m_ApplyWaitingInfo.Count <= 0 || m_CurrentGenerateReadWriteVersionListLength >= m_GenerateReadWriteVersionListLength)
{
GenerateReadWriteVersionList();
return true;
}
return false;
}
catch (Exception exception)
{
if (ResourceApplyFailure != null)
{
ResourceApplyFailure(applyInfo.ResourceName, m_ApplyingResourcePackPath, exception.ToString());
}
m_FailureFlag = true;
return false;
}
finally
{
m_ApplyingResourcePackStream.Position = position;
if (m_ResourceManager.m_DecompressCachedStream != null)
{
m_ResourceManager.m_DecompressCachedStream.Position = 0L;
m_ResourceManager.m_DecompressCachedStream.SetLength(0L);
}
}
}
private bool DownloadResource(UpdateInfo updateInfo)
{
if (updateInfo.Downloading)
{
return false;
}
updateInfo.Downloading = true;
string resourceFullNameWithCrc32 = updateInfo.ResourceName.Variant != null ? Utility.Text.Format("{0}.{1}.{2:x8}.{3}", updateInfo.ResourceName.Name, updateInfo.ResourceName.Variant, updateInfo.HashCode, DefaultExtension) : Utility.Text.Format("{0}.{1:x8}.{2}", updateInfo.ResourceName.Name, updateInfo.HashCode, DefaultExtension);
m_DownloadManager.AddDownload(updateInfo.ResourcePath, Utility.Path.GetRemotePath(Path.Combine(m_ResourceManager.m_UpdatePrefixUri, resourceFullNameWithCrc32)), updateInfo);
return true;
}
private void GenerateReadWriteVersionList()
{
FileStream fileStream = null;
try
{
fileStream = new FileStream(m_ReadWriteVersionListTempFileName, FileMode.Create, FileAccess.Write);
LocalVersionList.Resource[] resources = m_ResourceManager.m_ReadWriteResourceInfos.Count > 0 ? new LocalVersionList.Resource[m_ResourceManager.m_ReadWriteResourceInfos.Count] : null;
if (resources != null)
{
int index = 0;
foreach (KeyValuePair i in m_ResourceManager.m_ReadWriteResourceInfos)
{
resources[index] = new LocalVersionList.Resource(i.Key.Name, i.Key.Variant, i.Key.Extension, (byte)i.Value.LoadType, i.Value.Length, i.Value.HashCode);
if (i.Value.UseFileSystem)
{
List resourceIndexes = null;
if (!m_CachedFileSystemsForGenerateReadWriteVersionList.TryGetValue(i.Value.FileSystemName, out resourceIndexes))
{
resourceIndexes = new List();
m_CachedFileSystemsForGenerateReadWriteVersionList.Add(i.Value.FileSystemName, resourceIndexes);
}
resourceIndexes.Add(index);
}
index++;
}
}
LocalVersionList.FileSystem[] fileSystems = m_CachedFileSystemsForGenerateReadWriteVersionList.Count > 0 ? new LocalVersionList.FileSystem[m_CachedFileSystemsForGenerateReadWriteVersionList.Count] : null;
if (fileSystems != null)
{
int index = 0;
foreach (KeyValuePair> i in m_CachedFileSystemsForGenerateReadWriteVersionList)
{
fileSystems[index++] = new LocalVersionList.FileSystem(i.Key, i.Value.ToArray());
i.Value.Clear();
}
}
LocalVersionList versionList = new LocalVersionList(resources, fileSystems);
if (!m_ResourceManager.m_ReadWriteVersionListSerializer.Serialize(fileStream, versionList))
{
throw new GameFrameworkException("Serialize read-write version list failure.");
}
if (fileStream != null)
{
fileStream.Dispose();
fileStream = null;
}
}
catch (Exception exception)
{
if (fileStream != null)
{
fileStream.Dispose();
fileStream = null;
}
if (File.Exists(m_ReadWriteVersionListTempFileName))
{
File.Delete(m_ReadWriteVersionListTempFileName);
}
throw new GameFrameworkException(Utility.Text.Format("Generate read-write version list exception '{0}'.", exception), exception);
}
if (File.Exists(m_ReadWriteVersionListFileName))
{
File.Delete(m_ReadWriteVersionListFileName);
}
File.Move(m_ReadWriteVersionListTempFileName, m_ReadWriteVersionListFileName);
m_CurrentGenerateReadWriteVersionListLength = 0;
}
private void OnDownloadStart(object sender, DownloadStartEventArgs e)
{
UpdateInfo updateInfo = e.UserData as UpdateInfo;
if (updateInfo == null)
{
return;
}
if (m_DownloadManager == null)
{
throw new GameFrameworkException("You must set download manager first.");
}
if (e.CurrentLength > int.MaxValue)
{
throw new GameFrameworkException(Utility.Text.Format("File '{0}' is too large.", e.DownloadPath));
}
if (ResourceUpdateStart != null)
{
ResourceUpdateStart(updateInfo.ResourceName, e.DownloadPath, e.DownloadUri, (int)e.CurrentLength, updateInfo.CompressedLength, updateInfo.RetryCount);
}
}
private void OnDownloadUpdate(object sender, DownloadUpdateEventArgs e)
{
UpdateInfo updateInfo = e.UserData as UpdateInfo;
if (updateInfo == null)
{
return;
}
if (m_DownloadManager == null)
{
throw new GameFrameworkException("You must set download manager first.");
}
if (e.CurrentLength > updateInfo.CompressedLength)
{
m_DownloadManager.RemoveDownload(e.SerialId);
string downloadFile = Utility.Text.Format("{0}.download", e.DownloadPath);
if (File.Exists(downloadFile))
{
File.Delete(downloadFile);
}
string errorMessage = Utility.Text.Format("When download update, downloaded length is larger than compressed length, need '{0}', downloaded '{1}'.", updateInfo.CompressedLength, e.CurrentLength);
DownloadFailureEventArgs downloadFailureEventArgs = DownloadFailureEventArgs.Create(e.SerialId, e.DownloadPath, e.DownloadUri, errorMessage, e.UserData);
OnDownloadFailure(this, downloadFailureEventArgs);
ReferencePool.Release(downloadFailureEventArgs);
return;
}
if (ResourceUpdateChanged != null)
{
ResourceUpdateChanged(updateInfo.ResourceName, e.DownloadPath, e.DownloadUri, (int)e.CurrentLength, updateInfo.CompressedLength);
}
}
private void OnDownloadSuccess(object sender, DownloadSuccessEventArgs e)
{
UpdateInfo updateInfo = e.UserData as UpdateInfo;
if (updateInfo == null)
{
return;
}
try
{
using (FileStream fileStream = new FileStream(e.DownloadPath, FileMode.Open, FileAccess.ReadWrite))
{
bool compressed = updateInfo.Length != updateInfo.CompressedLength || updateInfo.HashCode != updateInfo.CompressedHashCode;
int length = (int)fileStream.Length;
if (length != updateInfo.CompressedLength)
{
fileStream.Close();
string errorMessage = Utility.Text.Format("Resource compressed length error, need '{0}', downloaded '{1}'.", updateInfo.CompressedLength, length);
DownloadFailureEventArgs downloadFailureEventArgs = DownloadFailureEventArgs.Create(e.SerialId, e.DownloadPath, e.DownloadUri, errorMessage, e.UserData);
OnDownloadFailure(this, downloadFailureEventArgs);
ReferencePool.Release(downloadFailureEventArgs);
return;
}
if (compressed)
{
fileStream.Position = 0L;
int hashCode = Utility.Verifier.GetCrc32(fileStream);
if (hashCode != updateInfo.CompressedHashCode)
{
fileStream.Close();
string errorMessage = Utility.Text.Format("Resource compressed hash code error, need '{0}', downloaded '{1}'.", updateInfo.CompressedHashCode, hashCode);
DownloadFailureEventArgs downloadFailureEventArgs = DownloadFailureEventArgs.Create(e.SerialId, e.DownloadPath, e.DownloadUri, errorMessage, e.UserData);
OnDownloadFailure(this, downloadFailureEventArgs);
ReferencePool.Release(downloadFailureEventArgs);
return;
}
if (m_ResourceManager.m_DecompressCachedStream == null)
{
m_ResourceManager.m_DecompressCachedStream = new MemoryStream();
}
fileStream.Position = 0L;
m_ResourceManager.m_DecompressCachedStream.Position = 0L;
m_ResourceManager.m_DecompressCachedStream.SetLength(0L);
if (!Utility.Compression.Decompress(fileStream, m_ResourceManager.m_DecompressCachedStream))
{
fileStream.Close();
string errorMessage = Utility.Text.Format("Unable to decompress resource '{0}'.", e.DownloadPath);
DownloadFailureEventArgs downloadFailureEventArgs = DownloadFailureEventArgs.Create(e.SerialId, e.DownloadPath, e.DownloadUri, errorMessage, e.UserData);
OnDownloadFailure(this, downloadFailureEventArgs);
ReferencePool.Release(downloadFailureEventArgs);
return;
}
if (m_ResourceManager.m_DecompressCachedStream.Length != updateInfo.Length)
{
fileStream.Close();
string errorMessage = Utility.Text.Format("Resource length error, need '{0}', downloaded '{1}'.", updateInfo.Length, m_ResourceManager.m_DecompressCachedStream.Length);
DownloadFailureEventArgs downloadFailureEventArgs = DownloadFailureEventArgs.Create(e.SerialId, e.DownloadPath, e.DownloadUri, errorMessage, e.UserData);
OnDownloadFailure(this, downloadFailureEventArgs);
ReferencePool.Release(downloadFailureEventArgs);
return;
}
fileStream.Position = 0L;
fileStream.SetLength(0L);
fileStream.Write(m_ResourceManager.m_DecompressCachedStream.GetBuffer(), 0, (int)m_ResourceManager.m_DecompressCachedStream.Length);
}
else
{
int hashCode = 0;
fileStream.Position = 0L;
if (updateInfo.LoadType == LoadType.LoadFromMemoryAndQuickDecrypt || updateInfo.LoadType == LoadType.LoadFromMemoryAndDecrypt
|| updateInfo.LoadType == LoadType.LoadFromBinaryAndQuickDecrypt || updateInfo.LoadType == LoadType.LoadFromBinaryAndDecrypt)
{
Utility.Converter.GetBytes(updateInfo.HashCode, m_CachedHashBytes);
if (updateInfo.LoadType == LoadType.LoadFromMemoryAndQuickDecrypt || updateInfo.LoadType == LoadType.LoadFromBinaryAndQuickDecrypt)
{
hashCode = Utility.Verifier.GetCrc32(fileStream, m_CachedHashBytes, Utility.Encryption.QuickEncryptLength);
}
else if (updateInfo.LoadType == LoadType.LoadFromMemoryAndDecrypt || updateInfo.LoadType == LoadType.LoadFromBinaryAndDecrypt)
{
hashCode = Utility.Verifier.GetCrc32(fileStream, m_CachedHashBytes, length);
}
Array.Clear(m_CachedHashBytes, 0, CachedHashBytesLength);
}
else
{
hashCode = Utility.Verifier.GetCrc32(fileStream);
}
if (hashCode != updateInfo.HashCode)
{
fileStream.Close();
string errorMessage = Utility.Text.Format("Resource hash code error, need '{0}', downloaded '{1}'.", updateInfo.HashCode, hashCode);
DownloadFailureEventArgs downloadFailureEventArgs = DownloadFailureEventArgs.Create(e.SerialId, e.DownloadPath, e.DownloadUri, errorMessage, e.UserData);
OnDownloadFailure(this, downloadFailureEventArgs);
ReferencePool.Release(downloadFailureEventArgs);
return;
}
}
}
if (updateInfo.UseFileSystem)
{
IFileSystem fileSystem = m_ResourceManager.GetFileSystem(updateInfo.FileSystemName, false);
bool retVal = fileSystem.WriteFile(updateInfo.ResourceName.FullName, updateInfo.ResourcePath);
if (File.Exists(updateInfo.ResourcePath))
{
File.Delete(updateInfo.ResourcePath);
}
if (!retVal)
{
string errorMessage = Utility.Text.Format("Write resource to file system '{0}' error.", fileSystem.FullPath);
DownloadFailureEventArgs downloadFailureEventArgs = DownloadFailureEventArgs.Create(e.SerialId, e.DownloadPath, e.DownloadUri, errorMessage, e.UserData);
OnDownloadFailure(this, downloadFailureEventArgs);
ReferencePool.Release(downloadFailureEventArgs);
return;
}
}
m_UpdateCandidateInfo.Remove(updateInfo.ResourceName);
m_UpdateWaitingInfo.Remove(updateInfo);
m_UpdateWaitingInfoWhilePlaying.Remove(updateInfo);
m_ResourceManager.m_ResourceInfos[updateInfo.ResourceName].MarkReady();
m_ResourceManager.m_ReadWriteResourceInfos.Add(updateInfo.ResourceName, new ReadWriteResourceInfo(updateInfo.FileSystemName, updateInfo.LoadType, updateInfo.Length, updateInfo.HashCode));
if (ResourceUpdateSuccess != null)
{
ResourceUpdateSuccess(updateInfo.ResourceName, e.DownloadPath, e.DownloadUri, updateInfo.Length, updateInfo.CompressedLength);
}
m_CurrentGenerateReadWriteVersionListLength += updateInfo.CompressedLength;
if (m_UpdateCandidateInfo.Count <= 0 || m_UpdateWaitingInfo.Count + m_UpdateWaitingInfoWhilePlaying.Count <= 0 || m_CurrentGenerateReadWriteVersionListLength >= m_GenerateReadWriteVersionListLength)
{
GenerateReadWriteVersionList();
}
if (m_UpdatingResourceGroup != null && m_UpdateWaitingInfo.Count <= 0)
{
ResourceGroup updatingResourceGroup = m_UpdatingResourceGroup;
m_UpdatingResourceGroup = null;
if (ResourceUpdateComplete != null)
{
ResourceUpdateComplete(updatingResourceGroup, !m_FailureFlag);
}
}
if (m_UpdateCandidateInfo.Count <= 0 && ResourceUpdateAllComplete != null)
{
ResourceUpdateAllComplete();
}
}
catch (Exception exception)
{
string errorMessage = Utility.Text.Format("Update resource '{0}' with error message '{1}'.", e.DownloadPath, exception);
DownloadFailureEventArgs downloadFailureEventArgs = DownloadFailureEventArgs.Create(e.SerialId, e.DownloadPath, e.DownloadUri, errorMessage, e.UserData);
OnDownloadFailure(this, downloadFailureEventArgs);
ReferencePool.Release(downloadFailureEventArgs);
}
finally
{
if (m_ResourceManager.m_DecompressCachedStream != null)
{
m_ResourceManager.m_DecompressCachedStream.Position = 0L;
m_ResourceManager.m_DecompressCachedStream.SetLength(0L);
}
}
}
private void OnDownloadFailure(object sender, DownloadFailureEventArgs e)
{
UpdateInfo updateInfo = e.UserData as UpdateInfo;
if (updateInfo == null)
{
return;
}
if (File.Exists(e.DownloadPath))
{
File.Delete(e.DownloadPath);
}
if (ResourceUpdateFailure != null)
{
ResourceUpdateFailure(updateInfo.ResourceName, e.DownloadUri, updateInfo.RetryCount, m_UpdateRetryCount, e.ErrorMessage);
}
if (updateInfo.RetryCount < m_UpdateRetryCount)
{
updateInfo.Downloading = false;
updateInfo.RetryCount++;
if (m_UpdateWaitingInfoWhilePlaying.Contains(updateInfo))
{
DownloadResource(updateInfo);
}
}
else
{
m_FailureFlag = true;
updateInfo.Downloading = false;
updateInfo.RetryCount = 0;
m_UpdateWaitingInfo.Remove(updateInfo);
m_UpdateWaitingInfoWhilePlaying.Remove(updateInfo);
}
}
}
}
}