Implement msu1 handling for bsnes115+ (#3190)
* Implement msu1 handling for bsnes115+ * RomPath->RomDirectory for clarity * Remove unnecessary include
This commit is contained in:
parent
7f99c11cce
commit
339d34413f
Binary file not shown.
|
@ -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
|
||||
}
|
||||
},
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -87,6 +87,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.BSNES
|
|||
|
||||
Api.Dispose();
|
||||
_resampler.Dispose();
|
||||
_currentMsuTrack?.Dispose();
|
||||
|
||||
_disposed = true;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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 {}
|
||||
};
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue