Implement msu1 handling for bsnes115+ (#3190)

* Implement msu1 handling for bsnes115+

* RomPath->RomDirectory for clarity

* Remove unnecessary include
This commit is contained in:
Moritz Bender 2022-03-28 23:52:13 +02:00 committed by GitHub
parent 7f99c11cce
commit 339d34413f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 142 additions and 12 deletions

Binary file not shown.

View File

@ -29,6 +29,7 @@ namespace BizHawk.Client.Common
public byte[] RomData { get; set; }
public byte[] FileData { get; set; }
public string Extension { get; set; }
public string RomDirectory { get; set; }
public GameInfo Game { get; set; }
}
private class CoreInventoryParameters : ICoreInventoryParameters
@ -481,6 +482,7 @@ namespace BizHawk.Client.Common
RomData = rom.RomData,
FileData = rom.FileData,
Extension = rom.Extension,
RomDirectory = file.Directory,
Game = game
}
},

View File

@ -67,6 +67,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.BSNES
public abstract void snes_set_cpu_register(string register, uint value);
[BizImport(CallingConvention.Cdecl)]
public abstract bool snes_cpu_step();
[BizImport(CallingConvention.Cdecl)]
public abstract bool snes_msu_sync();
}
public unsafe partial class BsnesApi : IDisposable, IMonitor, IStatable
@ -99,6 +102,22 @@ namespace BizHawk.Emulation.Cores.Nintendo.BSNES
}
}
public void AddReadonlyFile(string path, string name)
{
if (!_readonlyFiles.Contains(name))
{
try
{
exe.AddReadonlyFile(File.ReadAllBytes(path), name);
_readonlyFiles.Add(name);
}
catch
{
// ignored
}
}
}
public void SetCallbacks(SnesCallbacks callbacks)
{
var functionPointerArray = callbacks
@ -154,6 +173,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.BSNES
public delegate void snes_read_hook_t(uint address);
public delegate void snes_write_hook_t(uint address, byte value);
public delegate void snes_exec_hook_t(uint address);
public delegate void snes_msu_open_t(ushort track_id);
public delegate void snes_msu_seek_t(long offset, bool relative);
public delegate byte snes_msu_read_t();
public delegate bool snes_msu_end_t();
[StructLayout(LayoutKind.Sequential)]
public struct CpuRegisters
@ -201,6 +224,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.BSNES
public snes_read_hook_t readHookCb;
public snes_write_hook_t writeHookCb;
public snes_exec_hook_t execHookCb;
public snes_msu_open_t msuOpenCb;
public snes_msu_seek_t msuSeekCb;
public snes_msu_read_t msuReadCb;
public snes_msu_end_t msuEndCb;
private static List<FieldInfo> FieldsInOrder;
@ -229,11 +256,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.BSNES
public void Seal()
{
exe.Seal();
foreach (var s in _readonlyFiles)
foreach (string s in _readonlyFiles.Where(s => !s.StartsWith("msu1/")))
{
exe.RemoveReadonlyFile(s);
}
_readonlyFiles.Clear();
_readonlyFiles.RemoveAll(s => !s.StartsWith("msu1/"));
}
// TODO: confirm that the serializedSize is CONSTANT for any given game,
@ -258,6 +286,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.BSNES
// byte[] serializedData = reader.ReadBytes(serializedSize);
// _core.snes_unserialize(serializedData, serializedSize);
exe.LoadStateBinary(reader);
core.snes_msu_sync();
}
}
}

View File

@ -87,6 +87,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.BSNES
Api.Dispose();
_resampler.Dispose();
_currentMsuTrack?.Dispose();
_disposed = true;
}

View File

@ -19,6 +19,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.BSNES
var ser = new BasicServiceProvider(this);
ServiceProvider = ser;
this._romPath = Path.Combine(loadParameters.Roms[0].RomDirectory, loadParameters.Game.Name);
CoreComm = loadParameters.Comm;
_settings = loadParameters.Settings ?? new SnesSettings();
_syncSettings = loadParameters.SyncSettings ?? new SnesSyncSettings();
@ -50,7 +51,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.BSNES
traceCb = snes_trace,
readHookCb = ReadHook,
writeHookCb = WriteHook,
execHookCb = ExecHook
execHookCb = ExecHook,
msuOpenCb = msu_open,
msuSeekCb = msu_seek,
msuReadCb = msu_read,
msuEndCb = msu_end
};
Api = new BsnesApi(CoreComm.CoreFileProvider.DllPath(), CoreComm, callbacks.AllDelegatesInMemoryOrder());
@ -118,6 +123,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.BSNES
private IController _controller;
private SpeexResampler _resampler;
private readonly string _romPath;
private bool _disposed;
public bool IsSGB { get; }
@ -131,11 +137,18 @@ namespace BizHawk.Emulation.Cores.Nintendo.BSNES
private string snes_path_request(int slot, string hint, bool required)
{
if (hint == "save.ram")
switch (hint)
{
// core asked for saveram, but the interface isn't designed to be able to handle this.
// so, we'll just return nothing and the frontend will set the saveram itself later
return null;
case "manifest.bml":
Api.AddReadonlyFile($"{_romPath}.bml", hint);
return hint;
case "msu1/data.rom":
Api.AddReadonlyFile($"{_romPath}.msu", hint);
return hint;
case "save.ram":
// core asked for saveram, but the interface isn't designed to be able to handle this.
// so, we'll just return nothing and the frontend will set the saveram itself later
return null;
}
string firmwareId;
@ -303,5 +316,33 @@ namespace BizHawk.Emulation.Cores.Nintendo.BSNES
MemoryCallbacks.CallMemoryCallbacks(addr, 0, (uint) MemoryCallbackFlags.AccessExecute, "System Bus");
}
}
private FileStream _currentMsuTrack;
private void msu_seek(long offset, bool relative)
{
_currentMsuTrack?.Seek(offset, relative ? SeekOrigin.Current : SeekOrigin.Begin);
}
private byte msu_read()
{
return (byte) (_currentMsuTrack?.ReadByte() ?? 0);
}
private void msu_open(ushort trackId)
{
_currentMsuTrack?.Dispose();
try
{
_currentMsuTrack = File.OpenRead($"{_romPath}-{trackId}.pcm");
}
catch
{
_currentMsuTrack = null;
}
}
private bool msu_end()
{
return _currentMsuTrack.Position == _currentMsuTrack.Length;
}
}
}

View File

@ -26,6 +26,7 @@ namespace BizHawk.Emulation.Cores
public byte[] RomData { get; set; }
public byte[] FileData { get; set; }
public string Extension { get; set; }
public string RomDirectory { get; set; }
public GameInfo Game { get; set; }
}
@ -105,7 +106,7 @@ namespace BizHawk.Emulation.Cores
param.Roms = cip.Roms;
param.Discs = cip.Discs;
param.DeterministicEmulationRequested = cip.DeterministicEmulationRequested;
return (IEmulator)CTor.Invoke(new object[] { param });
return (IEmulator)CTor.Invoke(new object[] { param });
}
private IEmulator CreateUsingLegacyConstructorParameters(ICoreInventoryParameters cip)
@ -207,7 +208,7 @@ namespace BizHawk.Emulation.Cores
/// The user has indicated in preferences that this is their favorite core
/// </summary>
UserPreference = -200,
/// <summary>
/// A very good core that should be preferred over normal cores. Don't use this?
/// </summary>

View File

@ -9,6 +9,7 @@ namespace BizHawk.Emulation.Cores
byte[] RomData { get; }
byte[] FileData { get; }
string Extension { get; }
public string RomDirectory { get; }
/// <summary>
/// GameInfo for this individual asset. Doesn't make sense a lot of the time;
/// only use this if your individual rom assets are full proper games when considered alone.

View File

@ -0,0 +1,27 @@
#pragma once
#include <target-bsnescore/callbacks.h>
#include <nall/vfs.hpp>
namespace nall::vfs {
struct biz_file : file {
auto seek(intmax offset, index mode) -> void override {
return snesCallbacks.snes_msu_seek(offset, mode == index::relative);
}
auto read() -> uint8_t override {
return snesCallbacks.snes_msu_read();
}
auto end() const -> bool override {
return snesCallbacks.snes_msu_end();
}
// not implemented and not necessary, but must be overridden
auto size() const -> uintmax override { return -1; }
auto offset() const -> uintmax override { return -1; }
auto write(uint8_t data) -> void override {}
};
}

View File

@ -19,7 +19,7 @@ struct file {
virtual auto write(uint8_t data) -> void = 0;
virtual auto flush() -> void {}
auto end() const -> bool {
virtual auto end() const -> bool {
return offset() >= size();
}

View File

@ -450,3 +450,12 @@ EXPORT bool snes_cpu_step()
scheduler.StepOnce = false;
return scheduler.event == Scheduler::Event::Frame;
}
// should be called on savestate load, to get msu files loaded and in the correct state
EXPORT void snes_msu_sync()
{
if (cartridge.has.MSU1) {
msu1.dataOpen();
msu1.audioOpen();
}
}

View File

@ -13,6 +13,10 @@ typedef void (*snes_trace_t)(const char* disassembly, const char* register_info)
typedef void (*snes_read_hook_t)(uint32_t address);
typedef void (*snes_write_hook_t)(uint32_t address, uint8_t value);
typedef void (*snes_exec_hook_t)(uint32_t address);
typedef void (*snes_msu_open_t)(uint16_t track_id);
typedef void (*snes_msu_seek_t)(long offset, bool relative);
typedef uint8_t (*snes_msu_read_t)(void);
typedef bool (*snes_msu_end_t)(void);
struct SnesCallbacks {
snes_video_frame_t snes_video_frame;
@ -25,6 +29,10 @@ struct SnesCallbacks {
snes_read_hook_t snes_read_hook;
snes_write_hook_t snes_write_hook;
snes_exec_hook_t snes_exec_hook;
snes_msu_open_t snes_msu_open;
snes_msu_seek_t snes_msu_seek;
snes_msu_read_t snes_msu_read;
snes_msu_end_t snes_msu_end;
};
extern SnesCallbacks snesCallbacks;

View File

@ -7,6 +7,7 @@
#include <heuristics/bs-memory.cpp>
#include "resources.hpp"
#include <nall/vfs/biz_file.hpp>
static Emulator::Interface *emulator;
@ -150,11 +151,17 @@ auto Program::openFileSuperFamicom(string name, vfs::file::mode mode, bool requi
// TODO: the original bsnes code handles a lot more paths; *.data.ram, time.rtc and download.ram
// I believe none of these can currently be correctly served by bizhawk and I therefor ignore them here
// This should probably be changed? Not sure how much can break from not having them
if(name == "save.ram")
if(name == "msu1/data.rom" || name == "save.ram")
{
return vfs::fs::file::open(snesCallbacks.snes_path_request(ID::SuperFamicom, name, required), mode);
}
if(name.match("msu1/track*.pcm"))
{
snesCallbacks.snes_msu_open(strtol(name.data() + 11, nullptr, 10));
return shared_pointer<vfs::biz_file>{new vfs::biz_file};
}
if(name == "arm6.program.rom" && mode == File::Read) {
if(superFamicom.firmware.size() == 0x28000) {
return vfs::memory::file::open(&superFamicom.firmware.data()[0x00000], 0x20000);
@ -363,7 +370,11 @@ auto Program::loadSuperFamicom() -> bool
case 1: superFamicom.region = "NTSC"; break;
case 2: superFamicom.region = "PAL"; break;
}
superFamicom.manifest = heuristics.manifest();
if (auto fp = vfs::fs::file::open(snesCallbacks.snes_path_request(ID::SuperFamicom, "manifest.bml", false), File::Read)) {
superFamicom.manifest = fp->reads();
} else {
superFamicom.manifest = heuristics.manifest();
}
hackPatchMemory(rom);
superFamicom.document = BML::unserialize(superFamicom.manifest);