using AxibugEmuOnline.Client.ClientCore; using AxibugEmuOnline.Client.Event; using AxibugProtobuf; using ICSharpCode.SharpZipLib.Zip; using System; using System.Collections; using System.Collections.Generic; using System.IO; using UnityEngine; namespace AxibugEmuOnline.Client { public class RomFile { private HttpAPI.Resp_RomInfo webData; /// 依赖的Rom文件 private List dependencies = new List(); RomPlatformType m_defaultPlatform; /// 指示该Rom是否是多文件Rom public bool MultiFileRom { get { switch (Platform) { case RomPlatformType.Nes: return false; case RomPlatformType.Igs: case RomPlatformType.Cps1: case RomPlatformType.Cps2: case RomPlatformType.Neogeo: return true; default: throw new NotImplementedException($"未实现的平台{Platform}"); } } } bool m_hasLocalFile; public bool HasLocalFile { get { if (!m_hasLocalFile) return false; foreach (var depRom in dependencies) { if (!depRom.HasLocalFile) return false; } return true; } } /// 指示该Rom文件的存放路径 public string LocalFilePath => $"{App.PersistentDataPath(Platform)}/RemoteRoms/{FileName}"; /// 指示该Rom文件是否已下载完毕 public bool RomReady { get { if (!HasLocalFile) return false; foreach (var depRom in dependencies) { if (!depRom.RomReady) return false; } return true; } } /// 指示是否正在下载Rom文件 public bool IsDownloading { get { if (!InfoReady) return false; var progress = App.FileDownloader.GetDownloadProgress(webData.url); if (progress.HasValue) return true; foreach (var depRom in dependencies) { if (depRom.IsDownloading) return true; } return false; } } public float Progress { get { if (!IsDownloading) return 0; float progress = 0f; if (m_hasLocalFile) progress = 1f; else { var downloadProgress = App.FileDownloader.GetDownloadProgress(webData.url); progress = downloadProgress.HasValue ? downloadProgress.Value : 0; } foreach (var depRom in dependencies) { progress += depRom.Progress; } return progress / (dependencies.Count + 1); } } public RomPlatformType Platform => webData != null ? (RomPlatformType)webData.ptype : m_defaultPlatform; /// 指示该Rom信息是否已填充 public bool InfoReady { get { if (webData == null) return false; foreach (var depRom in dependencies) { if (!depRom.InfoReady) return false; } return true; } } /// 唯一标识 public int ID => webData != null ? webData.id : -1; /// 别名 public string Alias => webData.romName; /// 描述 public string Descript => webData.desc; /// 游戏类型 public string GameTypeDes => webData.gType; /// 小图URL public string ImageURL => webData.imgUrl; /// 文件名 public string FileName { get; private set; } /// 在查询结果中的索引 public int Index { get; private set; } /// 在查询结果中的所在页 public int Page { get; private set; } public string Hash => webData != null ? webData.hash : string.Empty; /// 标记是否收藏 public bool Star { get { return webData != null ? webData.isStar > 0 : false; } set { if (webData == null) return; webData.isStar = value ? 1 : 0; } } public event Action OnDownloadOver; public event Action OnInfoFilled; public RomFile(int index, int insidePage, RomPlatformType defaultPlatform) { Index = index; Page = insidePage; m_defaultPlatform = defaultPlatform; } public void CheckLocalFileState() { if (webData == null) m_hasLocalFile = false; else { if (App.FileDownloader.GetDownloadProgress(webData.url) == null) { if (MultiFileRom) m_hasLocalFile = Directory.Exists(LocalFilePath); else m_hasLocalFile = File.Exists(LocalFilePath); } } foreach (var depRom in dependencies) depRom.CheckLocalFileState(); } public void BeginDownload() { if (RomReady) return; if (IsDownloading) return; //检查依赖Rom的下载情况 foreach (var depRom in dependencies) { depRom.BeginDownload(); } App.FileDownloader.BeginDownload(webData.url, (bytes) => { HandleRomFilePostProcess(bytes); }); } private void HandleRomFilePostProcess(byte[] bytes) { if (bytes == null) return; if (MultiFileRom) { Dictionary unzipFiles = new Dictionary(); //多rom文件的平台,下载下来的数据直接解压放入文件夹内 var zip = new ZipInputStream(new MemoryStream(bytes)); List depth0Files = new List(); while (true) { var currentEntry = zip.GetNextEntry(); if (currentEntry == null) break; if (currentEntry.IsDirectory) continue; var buffer = new byte[1024]; MemoryStream output = new MemoryStream(); while (true) { var size = zip.Read(buffer, 0, buffer.Length); if (size == 0) break; else output.Write(buffer, 0, size); } output.Flush(); unzipFiles[$"{LocalFilePath}/{currentEntry.Name}"] = output.ToArray(); } string rootDirName = null; //如果第一层目录只有一个文件并且是文件夹,则所有文件层级外提一层 if (depth0Files.Count == 1 && depth0Files[0].Contains('.')) { rootDirName = depth0Files[0]; } foreach (var item in unzipFiles) { var path = rootDirName != null ? item.Key.Substring(0, rootDirName.Length + 1) : item.Key; var data = item.Value; Directory.CreateDirectory(Path.GetDirectoryName(path)); File.WriteAllBytes(path, data); } } else { var directPath = Path.GetDirectoryName(LocalFilePath); Directory.CreateDirectory(directPath); File.WriteAllBytes(LocalFilePath, bytes); } Eventer.Instance.PostEvent(EEvent.OnRomFileDownloaded, ID); OnDownloadOver?.Invoke(this); } public byte[] GetRomFileData() { Debug.Assert(!MultiFileRom, "仅供单文件Rom使用的接口"); if (webData == null) throw new Exception("Not Valid Rom"); if (!RomReady) throw new Exception("Rom File Not Downloaded"); var bytes = File.ReadAllBytes(LocalFilePath); if (Path.GetExtension(LocalFilePath).ToLower() == ".zip") { var zip = new ZipInputStream(new MemoryStream(bytes)); while (true) { var currentEntry = zip.GetNextEntry(); if (currentEntry == null) break; if (!currentEntry.Name.ToLower().EndsWith(".nes")) continue; var buffer = new byte[1024]; MemoryStream output = new MemoryStream(); while (true) { var size = zip.Read(buffer, 0, buffer.Length); if (size == 0) break; else output.Write(buffer, 0, size); } output.Flush(); return output.ToArray(); } } else { return bytes; } throw new Exception("Not Valid Rom Data"); } public void SetWebData(HttpAPI.Resp_RomInfo resp_RomInfo) { webData = resp_RomInfo; FileName = MultiFileRom ? Path.GetFileNameWithoutExtension(webData.url) : Path.GetFileName(webData.url); FileName = System.Net.WebUtility.UrlDecode(FileName); //收集依赖Rom if (webData.parentRomIdsList != null) { dependencies.Clear(); foreach (var romID in webData.parentRomIdsList) { var romFile = new RomFile(Index, Page, m_defaultPlatform); dependencies.Add(romFile); App.StartCoroutine(App.httpAPI.GetRomInfo(romID, (romInfo) => { romFile.SetWebData(romInfo); })); } } CheckLocalFileState(); App.StartCoroutine(WaitInfoReady()); } private IEnumerator WaitInfoReady() { while (!InfoReady) yield return null; OnInfoFilled?.Invoke(); } } }