diff --git a/AxibugEmuOnline.Client/Assets/Plugins/ICSharpCode.SharpZipLib.dll b/AxibugEmuOnline.Client/Assets/Plugins/ICSharpCode.SharpZipLib.dll new file mode 100644 index 0000000..11a8f2f Binary files /dev/null and b/AxibugEmuOnline.Client/Assets/Plugins/ICSharpCode.SharpZipLib.dll differ diff --git a/AxibugEmuOnline.Client/Assets/Plugins/SevenZipSharp.dll.meta b/AxibugEmuOnline.Client/Assets/Plugins/ICSharpCode.SharpZipLib.dll.meta similarity index 93% rename from AxibugEmuOnline.Client/Assets/Plugins/SevenZipSharp.dll.meta rename to AxibugEmuOnline.Client/Assets/Plugins/ICSharpCode.SharpZipLib.dll.meta index 3031b1e..ebefec2 100644 --- a/AxibugEmuOnline.Client/Assets/Plugins/SevenZipSharp.dll.meta +++ b/AxibugEmuOnline.Client/Assets/Plugins/ICSharpCode.SharpZipLib.dll.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: cda51a5b513e24a4db500829511ef182 +guid: 0f9e31e301641e94eb97c78b5c04d45b PluginImporter: externalObjects: {} serializedVersion: 2 diff --git a/AxibugEmuOnline.Client/Assets/Plugins/SevenZipSharp.dll b/AxibugEmuOnline.Client/Assets/Plugins/SevenZipSharp.dll deleted file mode 100644 index f588f8a..0000000 Binary files a/AxibugEmuOnline.Client/Assets/Plugins/SevenZipSharp.dll and /dev/null differ diff --git a/AxibugEmuOnline.Client/Assets/Script/AppAxibugEmuOnline.cs b/AxibugEmuOnline.Client/Assets/Script/AppAxibugEmuOnline.cs index 6211340..7fdf35f 100644 --- a/AxibugEmuOnline.Client/Assets/Script/AppAxibugEmuOnline.cs +++ b/AxibugEmuOnline.Client/Assets/Script/AppAxibugEmuOnline.cs @@ -36,6 +36,8 @@ namespace AxibugEmuOnline.Client.ClientCore user = new UserDataManager(); emu = new AppEmu(); netgame = new AppNetGame(); + romLib = new RomLib(); + httpAPI = new HttpAPI(); var go = new GameObject("[AppAxibugEmuOnline]"); GameObject.DontDestroyOnLoad(go); diff --git a/AxibugEmuOnline.Client/Assets/Script/Manager/HttpAPI.cs b/AxibugEmuOnline.Client/Assets/Script/Manager/HttpAPI.cs index 2d896ca..a1593d9 100644 --- a/AxibugEmuOnline.Client/Assets/Script/Manager/HttpAPI.cs +++ b/AxibugEmuOnline.Client/Assets/Script/Manager/HttpAPI.cs @@ -10,6 +10,7 @@ namespace AxibugEmuOnline.Client public class HttpAPI { public string WebSite = "http://emu.axibug.com/api"; + public string DownSite = "http://emu.axibug.com"; public void GetNesRomList(Action callback, int page, int pageSize = 10) { @@ -18,7 +19,7 @@ namespace AxibugEmuOnline.Client private IEnumerator GetNesRomListFlow(int page, int pageSize, Action callback) { - UnityWebRequest request = new UnityWebRequest($"{WebSite}/NesRomList?Page={page}&PageSize={pageSize}"); + UnityWebRequest request = UnityWebRequest.Get($"{WebSite}/NesRomList?Page={page}&PageSize={pageSize}"); yield return request.SendWebRequest(); if (request.result != UnityWebRequest.Result.Success) @@ -31,21 +32,48 @@ namespace AxibugEmuOnline.Client callback.Invoke(resp); } - public class Resp_GameList + enum GameType : byte { - public int Page { get; set; } - public int MaxPage { get; set; } - public int ResultAllCount { get; set; } - public List GameList { get; set; } + NONE = 0, + ACT, + ARPG, + AVG, + ETC, + FTG, + PUZ, + RAC, + RPG, + SLG, + SPG, + SRPG, + STG, + TAB, + /// + /// 合卡 + /// + ALLINONE, } + [Serializable] + public class Resp_GameList + { + public int page; + public int maxPage; + public int resultAllCount; + public List gameList; + } + + [Serializable] public class Resp_RomInfo { - public int ID { get; set; } - public string Hash { get; set; } - public string RomName { get; set; } - public string Url { get; set; } - public string ImgUrl { get; set; } + public int id; + public string romName; + public string gType; + public string desc; + public string url; + public string imgUrl; + public string hash; + public int stars; } } } diff --git a/AxibugEmuOnline.Client/Assets/Script/Manager/RomLib/RomFile.cs b/AxibugEmuOnline.Client/Assets/Script/Manager/RomLib/RomFile.cs index baf0957..850718d 100644 --- a/AxibugEmuOnline.Client/Assets/Script/Manager/RomLib/RomFile.cs +++ b/AxibugEmuOnline.Client/Assets/Script/Manager/RomLib/RomFile.cs @@ -1,6 +1,11 @@ -using System; +using AxibugEmuOnline.Client.ClientCore; +using ICSharpCode.SharpZipLib.Zip; +using System; +using System.Collections; using System.IO; +using System.Linq; using UnityEngine; +using UnityEngine.Networking; namespace AxibugEmuOnline.Client { @@ -8,34 +13,106 @@ namespace AxibugEmuOnline.Client { private HttpAPI.Resp_RomInfo webData; private bool hasLocalFile; - private string romName; + private string fileName; private EnumPlatform platform; - public string LocalFilePath => $"{Application.persistentDataPath}/RemoteRoms/{platform}/{romName}"; + public string LocalFilePath => $"{Application.persistentDataPath}/RemoteRoms/{platform}/{fileName}"; + public bool FileReady => hasLocalFile; + public int ID => webData != null ? webData.id : -1; + public bool IsDownloading { get; private set; } + public string Alias => webData.romName; + public string FileName => fileName; + public event Action OnDownloadOver; public RomFile(EnumPlatform platform) { this.platform = platform; } - public void GetRomFileData(Action callback) + public void BeginDownload() { - if (webData == null) { callback.Invoke(null); return; } - if (hasLocalFile) + if (FileReady) return; + if (IsDownloading) return; + + IsDownloading = true; + AppAxibugEmuOnline.StartCoroutine(DownloadRemoteRom((bytes) => { - var path = LocalFilePath; + IsDownloading = false; + + if (bytes != null) + { + var directPath = Path.GetDirectoryName(LocalFilePath); + Directory.CreateDirectory(directPath); + + File.WriteAllBytes(LocalFilePath, bytes); + hasLocalFile = true; + } + OnDownloadOver?.Invoke(); + })); + } + + public byte[] GetRomFileData() + { + if (webData == null) throw new Exception("Not Valid Rom"); + if (!FileReady) 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 (zip.GetNextEntry() is ZipEntry entry) + { + if (!entry.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"); + } + + private IEnumerator DownloadRemoteRom(Action callback) + { + UnityWebRequest uwr = UnityWebRequest.Get($"{AppAxibugEmuOnline.httpAPI.DownSite}/{webData.url}"); + yield return uwr.SendWebRequest(); + + if (uwr.result != UnityWebRequest.Result.Success) + { + callback(null); + } + else + { + callback(uwr.downloadHandler.data); } } public void SetWebData(HttpAPI.Resp_RomInfo resp_RomInfo) { webData = resp_RomInfo; - romName = Path.GetFileName(webData.Url); + fileName = Path.GetFileName(webData.url); if (File.Exists(LocalFilePath)) { - hasLocalFile = true; + var fileBytes = File.ReadAllBytes(LocalFilePath); + var localHash = RomLib.CalcHash(fileBytes); + + hasLocalFile = localHash == webData.hash; + if (!hasLocalFile) + File.Delete(LocalFilePath); } else { diff --git a/AxibugEmuOnline.Client/Assets/Script/Manager/RomLib/RomLib.cs b/AxibugEmuOnline.Client/Assets/Script/Manager/RomLib/RomLib.cs index f2988a6..351ae66 100644 --- a/AxibugEmuOnline.Client/Assets/Script/Manager/RomLib/RomLib.cs +++ b/AxibugEmuOnline.Client/Assets/Script/Manager/RomLib/RomLib.cs @@ -1,44 +1,66 @@ using AxibugEmuOnline.Client.ClientCore; using System; using System.Collections.Generic; +using System.Security.Cryptography; +using System.Text; +using UnityEngine; namespace AxibugEmuOnline.Client { public class RomLib { - private List nesRomFiles = new List(); + private Dictionary nesRomFileIdMapper = new Dictionary(); + private Dictionary nesRomFileNameMapper = new Dictionary(); - public void GetNesRomFile(int index, Action callback) + public RomFile GetNesRomFile(string romFileName) { - if (nesRomFiles.Count <= index) + nesRomFileNameMapper.TryGetValue(romFileName, out RomFile romFile); + return romFile; + } + + public void GetNesRomFile(int page, int pageSize, Action> callback) + { + AppAxibugEmuOnline.httpAPI.GetNesRomList((romList) => { - int pageSize = 10; - int page = index / pageSize; - int indexInPage = index % page; - - //填充空的RomFile数据 - var needFill = index - nesRomFiles.Count + 1; - needFill += pageSize - indexInPage - 1; - for (int i = 0; i < needFill; i++) + if (romList == null) { - nesRomFiles.Add(new RomFile(EnumPlatform.NES)); + callback.Invoke(null); } - - AppAxibugEmuOnline.httpAPI.GetNesRomList((romList) => + else { - if (romList == null) + List result = new List(); + for (int i = 0; i < romList.gameList.Count; i++) { - callback.Invoke(null); - } - else - { - for (int i = 0; i < romList.GameList.Count; i++) + var webData = romList.gameList[i]; + + nesRomFileIdMapper.TryGetValue(webData.id, out var targetRomFile); + if (targetRomFile == null) { - nesRomFiles[pageSize * (page - 1) + i].SetWebData(romList.GameList[i]); + targetRomFile = new RomFile(EnumPlatform.NES); + targetRomFile.SetWebData(webData); + nesRomFileIdMapper[webData.id] = targetRomFile; + + nesRomFileNameMapper[targetRomFile.FileName] = targetRomFile; } + + result.Add(targetRomFile); } - }, page, pageSize); + + callback(result); + } + }, page, pageSize); + } + + public static string CalcHash(byte[] data) + { + var hashBytes = MD5.Create().ComputeHash(data); + StringBuilder sb = new StringBuilder(); + foreach (byte b in hashBytes) + { + sb.Append(b.ToString("x2")); } + + return sb.ToString(); } } } diff --git a/AxibugEmuOnline.Client/Assets/Script/NesEmulator/CoreSupporter.cs b/AxibugEmuOnline.Client/Assets/Script/NesEmulator/CoreSupporter.cs index ae766d6..99e195b 100644 --- a/AxibugEmuOnline.Client/Assets/Script/NesEmulator/CoreSupporter.cs +++ b/AxibugEmuOnline.Client/Assets/Script/NesEmulator/CoreSupporter.cs @@ -26,8 +26,10 @@ namespace AxibugEmuOnline.Client { try { - var stream = File.Open($"{RomDirectoryPath}/{fname}", FileMode.Open); - return stream; + var romFile = AppAxibugEmuOnline.romLib.GetNesRomFile(fname); + var bytes = romFile.GetRomFileData(); + Debug.Log($"Open {romFile.Alias}"); + return new MemoryStream(bytes); } catch (Exception ex) { diff --git a/AxibugEmuOnline.Client/Assets/Script/NesEmulator/NesEmulator.cs b/AxibugEmuOnline.Client/Assets/Script/NesEmulator/NesEmulator.cs index 1c8d76d..5341fbb 100644 --- a/AxibugEmuOnline.Client/Assets/Script/NesEmulator/NesEmulator.cs +++ b/AxibugEmuOnline.Client/Assets/Script/NesEmulator/NesEmulator.cs @@ -1,6 +1,7 @@ using AxibugEmuOnline.Client.ClientCore; using System; using System.IO; +using System.Linq; using System.Xml.Linq; using UnityEngine; using VirtualNes.Core; @@ -15,22 +16,14 @@ namespace AxibugEmuOnline.Client public VideoProvider VideoProvider; public AudioProvider AudioProvider; -#if UNITY_EDITOR - public string RomName; -#endif - private void Start() { Application.targetFrameRate = 60; VideoProvider.NesEmu = this; AudioProvider.NesEmu = this; - -#if UNITY_EDITOR - StartGame(RomName); -#endif } - public void StartGame(string romName) + public void StartGame(RomFile rom) { StopGame(); @@ -39,7 +32,7 @@ namespace AxibugEmuOnline.Client try { - NesCore = new NES(romName); + NesCore = new NES(rom.FileName); } catch (Exception ex) { @@ -92,6 +85,38 @@ namespace AxibugEmuOnline.Client UnityEditor.EditorUtility.SetDirty(db); UnityEditor.AssetDatabase.SaveAssets(); } + + [ContextMenu("LoadRom")] + public void LoadRom() + { + AppAxibugEmuOnline.romLib.GetNesRomFile(0, 10, (romFiles) => + { + if (romFiles == null) return; + + var file = romFiles[2]; + + if (file.FileReady) + { + StartGame(file); + } + else + { + file.BeginDownload(); + Action action = null; + action = () => + { + file.OnDownloadOver -= action; + if (!file.FileReady) + { + throw new Exception("Download Failed"); + } + StartGame(file); + }; + file.OnDownloadOver += action; + } + + }); + } #endif } } diff --git a/AxibugEmuOnline.Client/Assets/Script/NesEmulator/VideoProvider.cs b/AxibugEmuOnline.Client/Assets/Script/NesEmulator/VideoProvider.cs index 4fa0d3e..70ade97 100644 --- a/AxibugEmuOnline.Client/Assets/Script/NesEmulator/VideoProvider.cs +++ b/AxibugEmuOnline.Client/Assets/Script/NesEmulator/VideoProvider.cs @@ -2,7 +2,6 @@ using System; using System.Runtime.InteropServices; using UnityEngine; using UnityEngine.UI; -using VirtualNes.Core; namespace AxibugEmuOnline.Client { @@ -30,7 +29,8 @@ namespace AxibugEmuOnline.Client GCHandle handle = GCHandle.Alloc(wrapTexBuffer, GCHandleType.Pinned); // ȡָ wrapTexBufferPointer = handle.AddrOfPinnedObject(); - + + Image.texture = wrapTex; Image.material.SetTexture("_MainTex", wrapTex); TexBufferSize = wrapTexBuffer.Length * 4; @@ -43,9 +43,9 @@ namespace AxibugEmuOnline.Client uint colorRaw = palRaw[i]; var argbColor = BitConverter.GetBytes(colorRaw); Color temp = Color.white; - temp.r = argbColor[2]/255f; - temp.g = argbColor[1]/255f; - temp.b = argbColor[0]/255f; + temp.r = argbColor[2] / 255f; + temp.g = argbColor[1] / 255f; + temp.b = argbColor[0] / 255f; temp.a = 1; pPal.SetPixel(i, 0, temp); } diff --git a/AxibugEmuOnline.Client/Assets/Script/NesEmulator/VirtuaNesDraw.shader b/AxibugEmuOnline.Client/Assets/Script/NesEmulator/VirtuaNesDraw.shader index 5d58b04..63fad39 100644 --- a/AxibugEmuOnline.Client/Assets/Script/NesEmulator/VirtuaNesDraw.shader +++ b/AxibugEmuOnline.Client/Assets/Script/NesEmulator/VirtuaNesDraw.shader @@ -111,7 +111,7 @@ float rawIndex = color.b; - color = tex2D(_PalTex,float2(rawIndex,0.5)); + color = tex2D(_PalTex,float2(rawIndex,0.5)); #ifdef UNITY_UI_CLIP_RECT color.a *= UnityGet2DClipping(IN.worldPosition.xy, _ClipRect); diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PAD.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PAD.cs index e718c66..63a37f9 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PAD.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PAD.cs @@ -470,49 +470,6 @@ namespace VirtualNes.Core // Start if (state.HasButton(no, EnumButtonType.START)) bit |= 1 << 3; - // A rapid setup - if ((bit & (1 << 8)) != 0) - { - int spd = Supporter.Config.controller.nRapid[no][0]; - if (spd >= 3) spd = 3; - - int[] tbl = rentbl[spd]; - - if (padcnt[no][0] >= renmask[spd]) - padcnt[no][0] = 0; - - if ((tbl[padcnt[no][0]]) != 0) - bit |= (1 << 0); - else - bit = (byte)(bit & ~(1 << 0)); - - padcnt[no][0]++; - } - else - { - padcnt[no][0] = 0; - } - // B rapid setup - if ((bit & (1 << 9)) != 0) - { - int spd = Supporter.Config.controller.nRapid[no][1]; - if (spd >= 3) spd = 3; - int[] tbl = rentbl[spd]; - - if (padcnt[no][1] >= renmask[spd]) - padcnt[no][1] = 0; - - if ((tbl[padcnt[no][1]]) != 0) - bit |= (1 << 1); - else - bit = (byte)(bit & ~(1 << 1)); - - padcnt[no][1]++; - } - else - { - padcnt[no][1] = 0; - } return (byte)(bit & 0xFF); }