支持Http Rom仓库下载

This commit is contained in:
ALIENJACK\alien 2024-08-14 13:09:22 +08:00
parent 62377ab9d4
commit 360ea2f579
12 changed files with 217 additions and 104 deletions

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: cda51a5b513e24a4db500829511ef182 guid: 0f9e31e301641e94eb97c78b5c04d45b
PluginImporter: PluginImporter:
externalObjects: {} externalObjects: {}
serializedVersion: 2 serializedVersion: 2

View File

@ -36,6 +36,8 @@ namespace AxibugEmuOnline.Client.ClientCore
user = new UserDataManager(); user = new UserDataManager();
emu = new AppEmu(); emu = new AppEmu();
netgame = new AppNetGame(); netgame = new AppNetGame();
romLib = new RomLib();
httpAPI = new HttpAPI();
var go = new GameObject("[AppAxibugEmuOnline]"); var go = new GameObject("[AppAxibugEmuOnline]");
GameObject.DontDestroyOnLoad(go); GameObject.DontDestroyOnLoad(go);

View File

@ -10,6 +10,7 @@ namespace AxibugEmuOnline.Client
public class HttpAPI public class HttpAPI
{ {
public string WebSite = "http://emu.axibug.com/api"; public string WebSite = "http://emu.axibug.com/api";
public string DownSite = "http://emu.axibug.com";
public void GetNesRomList(Action<Resp_GameList> callback, int page, int pageSize = 10) public void GetNesRomList(Action<Resp_GameList> callback, int page, int pageSize = 10)
{ {
@ -18,7 +19,7 @@ namespace AxibugEmuOnline.Client
private IEnumerator GetNesRomListFlow(int page, int pageSize, Action<Resp_GameList> callback) private IEnumerator GetNesRomListFlow(int page, int pageSize, Action<Resp_GameList> 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(); yield return request.SendWebRequest();
if (request.result != UnityWebRequest.Result.Success) if (request.result != UnityWebRequest.Result.Success)
@ -31,21 +32,48 @@ namespace AxibugEmuOnline.Client
callback.Invoke(resp); callback.Invoke(resp);
} }
public class Resp_GameList enum GameType : byte
{ {
public int Page { get; set; } NONE = 0,
public int MaxPage { get; set; } ACT,
public int ResultAllCount { get; set; } ARPG,
public List<Resp_RomInfo> GameList { get; set; } AVG,
ETC,
FTG,
PUZ,
RAC,
RPG,
SLG,
SPG,
SRPG,
STG,
TAB,
/// <summary>
/// 合卡
/// </summary>
ALLINONE,
} }
[Serializable]
public class Resp_GameList
{
public int page;
public int maxPage;
public int resultAllCount;
public List<Resp_RomInfo> gameList;
}
[Serializable]
public class Resp_RomInfo public class Resp_RomInfo
{ {
public int ID { get; set; } public int id;
public string Hash { get; set; } public string romName;
public string RomName { get; set; } public string gType;
public string Url { get; set; } public string desc;
public string ImgUrl { get; set; } public string url;
public string imgUrl;
public string hash;
public int stars;
} }
} }
} }

View File

@ -1,6 +1,11 @@
using System; using AxibugEmuOnline.Client.ClientCore;
using ICSharpCode.SharpZipLib.Zip;
using System;
using System.Collections;
using System.IO; using System.IO;
using System.Linq;
using UnityEngine; using UnityEngine;
using UnityEngine.Networking;
namespace AxibugEmuOnline.Client namespace AxibugEmuOnline.Client
{ {
@ -8,34 +13,106 @@ namespace AxibugEmuOnline.Client
{ {
private HttpAPI.Resp_RomInfo webData; private HttpAPI.Resp_RomInfo webData;
private bool hasLocalFile; private bool hasLocalFile;
private string romName; private string fileName;
private EnumPlatform platform; 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) public RomFile(EnumPlatform platform)
{ {
this.platform = platform; this.platform = platform;
} }
public void GetRomFileData(Action<byte[]> callback) public void BeginDownload()
{ {
if (webData == null) { callback.Invoke(null); return; } if (FileReady) return;
if (hasLocalFile) 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<byte[]> 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) public void SetWebData(HttpAPI.Resp_RomInfo resp_RomInfo)
{ {
webData = resp_RomInfo; webData = resp_RomInfo;
romName = Path.GetFileName(webData.Url); fileName = Path.GetFileName(webData.url);
if (File.Exists(LocalFilePath)) 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 else
{ {

View File

@ -1,29 +1,25 @@
using AxibugEmuOnline.Client.ClientCore; using AxibugEmuOnline.Client.ClientCore;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;
using UnityEngine;
namespace AxibugEmuOnline.Client namespace AxibugEmuOnline.Client
{ {
public class RomLib public class RomLib
{ {
private List<RomFile> nesRomFiles = new List<RomFile>(); private Dictionary<int, RomFile> nesRomFileIdMapper = new Dictionary<int, RomFile>();
private Dictionary<string, RomFile> nesRomFileNameMapper = new Dictionary<string, RomFile>();
public void GetNesRomFile(int index, Action<RomFile> callback) public RomFile GetNesRomFile(string romFileName)
{ {
if (nesRomFiles.Count <= index) nesRomFileNameMapper.TryGetValue(romFileName, out RomFile romFile);
{ return romFile;
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++)
{
nesRomFiles.Add(new RomFile(EnumPlatform.NES));
} }
public void GetNesRomFile(int page, int pageSize, Action<List<RomFile>> callback)
{
AppAxibugEmuOnline.httpAPI.GetNesRomList((romList) => AppAxibugEmuOnline.httpAPI.GetNesRomList((romList) =>
{ {
if (romList == null) if (romList == null)
@ -32,13 +28,39 @@ namespace AxibugEmuOnline.Client
} }
else else
{ {
for (int i = 0; i < romList.GameList.Count; i++) List<RomFile> result = new List<RomFile>();
for (int i = 0; i < romList.gameList.Count; i++)
{ {
nesRomFiles[pageSize * (page - 1) + i].SetWebData(romList.GameList[i]); var webData = romList.gameList[i];
nesRomFileIdMapper.TryGetValue(webData.id, out var targetRomFile);
if (targetRomFile == null)
{
targetRomFile = new RomFile(EnumPlatform.NES);
targetRomFile.SetWebData(webData);
nesRomFileIdMapper[webData.id] = targetRomFile;
nesRomFileNameMapper[targetRomFile.FileName] = targetRomFile;
} }
result.Add(targetRomFile);
}
callback(result);
} }
}, page, pageSize); }, 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();
} }
} }
} }

View File

@ -26,8 +26,10 @@ namespace AxibugEmuOnline.Client
{ {
try try
{ {
var stream = File.Open($"{RomDirectoryPath}/{fname}", FileMode.Open); var romFile = AppAxibugEmuOnline.romLib.GetNesRomFile(fname);
return stream; var bytes = romFile.GetRomFileData();
Debug.Log($"Open {romFile.Alias}");
return new MemoryStream(bytes);
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@ -1,6 +1,7 @@
using AxibugEmuOnline.Client.ClientCore; using AxibugEmuOnline.Client.ClientCore;
using System; using System;
using System.IO; using System.IO;
using System.Linq;
using System.Xml.Linq; using System.Xml.Linq;
using UnityEngine; using UnityEngine;
using VirtualNes.Core; using VirtualNes.Core;
@ -15,22 +16,14 @@ namespace AxibugEmuOnline.Client
public VideoProvider VideoProvider; public VideoProvider VideoProvider;
public AudioProvider AudioProvider; public AudioProvider AudioProvider;
#if UNITY_EDITOR
public string RomName;
#endif
private void Start() private void Start()
{ {
Application.targetFrameRate = 60; Application.targetFrameRate = 60;
VideoProvider.NesEmu = this; VideoProvider.NesEmu = this;
AudioProvider.NesEmu = this; AudioProvider.NesEmu = this;
#if UNITY_EDITOR
StartGame(RomName);
#endif
} }
public void StartGame(string romName) public void StartGame(RomFile rom)
{ {
StopGame(); StopGame();
@ -39,7 +32,7 @@ namespace AxibugEmuOnline.Client
try try
{ {
NesCore = new NES(romName); NesCore = new NES(rom.FileName);
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -92,6 +85,38 @@ namespace AxibugEmuOnline.Client
UnityEditor.EditorUtility.SetDirty(db); UnityEditor.EditorUtility.SetDirty(db);
UnityEditor.AssetDatabase.SaveAssets(); 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 #endif
} }
} }

View File

@ -2,7 +2,6 @@ using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
using VirtualNes.Core;
namespace AxibugEmuOnline.Client namespace AxibugEmuOnline.Client
{ {
@ -31,6 +30,7 @@ namespace AxibugEmuOnline.Client
// »ñÈ¡Êý×éµÄÖ¸Õë // »ñÈ¡Êý×éµÄÖ¸Õë
wrapTexBufferPointer = handle.AddrOfPinnedObject(); wrapTexBufferPointer = handle.AddrOfPinnedObject();
Image.texture = wrapTex;
Image.material.SetTexture("_MainTex", wrapTex); Image.material.SetTexture("_MainTex", wrapTex);
TexBufferSize = wrapTexBuffer.Length * 4; TexBufferSize = wrapTexBuffer.Length * 4;
@ -43,9 +43,9 @@ namespace AxibugEmuOnline.Client
uint colorRaw = palRaw[i]; uint colorRaw = palRaw[i];
var argbColor = BitConverter.GetBytes(colorRaw); var argbColor = BitConverter.GetBytes(colorRaw);
Color temp = Color.white; Color temp = Color.white;
temp.r = argbColor[2]/255f; temp.r = argbColor[2] / 255f;
temp.g = argbColor[1]/255f; temp.g = argbColor[1] / 255f;
temp.b = argbColor[0]/255f; temp.b = argbColor[0] / 255f;
temp.a = 1; temp.a = 1;
pPal.SetPixel(i, 0, temp); pPal.SetPixel(i, 0, temp);
} }

View File

@ -470,49 +470,6 @@ namespace VirtualNes.Core
// Start // Start
if (state.HasButton(no, EnumButtonType.START)) bit |= 1 << 3; 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); return (byte)(bit & 0xFF);
} }