various bugfixes to system/save pathing and support CAN_DUPE, to stabilize the gambatte and neopop cores
This commit is contained in:
parent
ecee3365cc
commit
53fcc09c08
|
@ -32,6 +32,17 @@ namespace BizHawk.Client.Common
|
|||
return PathManager.SaveRamPath(Global.Game);
|
||||
}
|
||||
|
||||
|
||||
public string GetRetroSaveRAMDirectory()
|
||||
{
|
||||
return PathManager.RetroSaveRAMDirectory(Global.Game);
|
||||
}
|
||||
|
||||
public string GetRetroSystemPath()
|
||||
{
|
||||
return PathManager.RetroSystemPath(Global.Game);
|
||||
}
|
||||
|
||||
public string GetGameBasePath()
|
||||
{
|
||||
return PathManager.GetGameBasePath(Global.Game);
|
||||
|
|
|
@ -292,6 +292,38 @@ namespace BizHawk.Client.Common
|
|||
return Path.Combine(MakeAbsolutePath(pathEntry.Path, game.System), name) + ".SaveRAM";
|
||||
}
|
||||
|
||||
public static string RetroSaveRAMDirectory(GameInfo game)
|
||||
{
|
||||
//hijinx here to get the core name out of the game name
|
||||
var name = FilesystemSafeName(game);
|
||||
name = Path.GetDirectoryName(name);
|
||||
if (name == "") name = FilesystemSafeName(game);
|
||||
|
||||
if (Global.MovieSession.Movie.IsActive)
|
||||
{
|
||||
name = Path.Combine(name, "movie-" + Path.GetFileNameWithoutExtension(Global.MovieSession.Movie.Filename));
|
||||
}
|
||||
|
||||
var pathEntry = Global.Config.PathEntries[game.System, "Save RAM"] ??
|
||||
Global.Config.PathEntries[game.System, "Base"];
|
||||
|
||||
return Path.Combine(MakeAbsolutePath(pathEntry.Path, game.System), name);
|
||||
}
|
||||
|
||||
|
||||
public static string RetroSystemPath(GameInfo game)
|
||||
{
|
||||
//hijinx here to get the core name out of the game name
|
||||
var name = FilesystemSafeName(game);
|
||||
name = Path.GetDirectoryName(name);
|
||||
if(name == "") name = FilesystemSafeName(game);
|
||||
|
||||
var pathEntry = Global.Config.PathEntries[game.System, "System"] ??
|
||||
Global.Config.PathEntries[game.System, "Base"];
|
||||
|
||||
return Path.Combine(MakeAbsolutePath(pathEntry.Path, game.System), name);
|
||||
}
|
||||
|
||||
public static string GetGameBasePath(GameInfo game)
|
||||
{
|
||||
var name = FilesystemSafeName(game);
|
||||
|
|
|
@ -255,39 +255,49 @@ namespace BizHawk.Client.Common
|
|||
|
||||
if (AsLibretro)
|
||||
{
|
||||
//we'll need to generate a game name for purposes of state/saveram pathing etc.
|
||||
string gameName;
|
||||
|
||||
string codePathPart = Path.GetFileNameWithoutExtension(nextComm.LaunchLibretroCore);
|
||||
|
||||
var retro = new LibRetroEmulator(nextComm, nextComm.LaunchLibretroCore);
|
||||
nextEmulator = retro;
|
||||
|
||||
//kind of dirty.. we need to stash this, and then we can unstash it in a moment, in case the core doesnt fail
|
||||
var oldGame = Global.Game;
|
||||
|
||||
if (retro.Description.SupportsNoGame && string.IsNullOrEmpty(path))
|
||||
{
|
||||
//must be done before LoadNoGame (which triggers retro_init and the paths to be consumed by the core)
|
||||
//game name == name of core
|
||||
var gameName = codePathPart;
|
||||
Global.Game = game = new GameInfo { Name = gameName, System = "Libretro" };
|
||||
|
||||
//if we are allowed to run NoGame and we dont have a game, boot up the core that way
|
||||
bool ret = retro.LoadNoGame();
|
||||
|
||||
Global.Game = oldGame;
|
||||
|
||||
if (!ret)
|
||||
{
|
||||
DoLoadErrorCallback("LibretroNoGame failed to load. This is weird", "Libretro");
|
||||
retro.Dispose();
|
||||
return false;
|
||||
}
|
||||
|
||||
//game name == name of core
|
||||
gameName = codePathPart;
|
||||
}
|
||||
else
|
||||
{
|
||||
bool ret;
|
||||
|
||||
//must be done before LoadNoGame (which triggers retro_init and the paths to be consumed by the core)
|
||||
//game name == name of core + extensionless_game_filename
|
||||
var gameName = Path.Combine(codePathPart, Path.GetFileNameWithoutExtension(file.Name));
|
||||
Global.Game = game = new GameInfo { Name = gameName, System = "Libretro" };
|
||||
|
||||
//if the core requires an archive file, then try passing the filename of the archive
|
||||
//(but do we ever need to actually load the contents of the archive file into ram?)
|
||||
if (retro.Description.NeedsArchives)
|
||||
{
|
||||
if (file.IsArchiveMember)
|
||||
throw new InvalidOperationException("Should not have bound file member for libretro block_extract core");
|
||||
retro.LoadPath(file.FullPathWithoutMember);
|
||||
ret = retro.LoadPath(file.FullPathWithoutMember);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -303,21 +313,18 @@ namespace BizHawk.Client.Common
|
|||
if (ret)
|
||||
ret = retro.LoadData(file.ReadAllBytes());
|
||||
}
|
||||
|
||||
if (!ret)
|
||||
{
|
||||
DoLoadErrorCallback("Libretro failed to load the given file. This is probably due to a core/content mismatch. Moreover, the process is now likely to be hosed. We suggest you restart the program.", "Libretro");
|
||||
retro.Dispose();
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//game name == name of core + extensionless_game_filename
|
||||
gameName = Path.Combine(codePathPart, Path.GetFileNameWithoutExtension(file.Name));
|
||||
Global.Game = oldGame;
|
||||
|
||||
if (!ret)
|
||||
{
|
||||
DoLoadErrorCallback("Libretro failed to load the given file. This is probably due to a core/content mismatch. Moreover, the process is now likely to be hosed. We suggest you restart the program.", "Libretro");
|
||||
retro.Dispose();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
game = new GameInfo { Name = gameName, System = "Libretro" };
|
||||
|
||||
}
|
||||
else
|
||||
|
|
|
@ -314,10 +314,11 @@ namespace BizHawk.Client.Common
|
|||
|
||||
new PathEntry { System = "Libretro", SystemDisplayName = "Libretro", Type = "Base", Path = Path.Combine(".", "Libretro"), Ordinal = 0 },
|
||||
new PathEntry { System = "Libretro", SystemDisplayName = "Libretro", Type = "Cores", Path = Path.Combine(".", "Cores"), Ordinal = 1 },
|
||||
new PathEntry { System = "Libretro", SystemDisplayName = "Libretro", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 },
|
||||
new PathEntry { System = "Libretro", SystemDisplayName = "Libretro", Type = "Save RAM", Path = Path.Combine(".", "SaveRAM"), Ordinal = 3 },
|
||||
new PathEntry { System = "Libretro", SystemDisplayName = "Libretro", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 },
|
||||
new PathEntry { System = "Libretro", SystemDisplayName = "Libretro", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 },
|
||||
new PathEntry { System = "Libretro", SystemDisplayName = "Libretro", Type = "System", Path = Path.Combine(".", "System"), Ordinal = 2 },
|
||||
new PathEntry { System = "Libretro", SystemDisplayName = "Libretro", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 3 },
|
||||
new PathEntry { System = "Libretro", SystemDisplayName = "Libretro", Type = "Save RAM", Path = Path.Combine(".", "SaveRAM"), Ordinal = 4 },
|
||||
new PathEntry { System = "Libretro", SystemDisplayName = "Libretro", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 5 },
|
||||
new PathEntry { System = "Libretro", SystemDisplayName = "Libretro", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 6 },
|
||||
|
||||
};
|
||||
}
|
||||
|
|
|
@ -72,7 +72,10 @@ namespace BizHawk.Client.EmuHawk
|
|||
//scan the current libretro core to see if it can be launched with NoGame,and other stuff
|
||||
try
|
||||
{
|
||||
using (var retro = new LibRetroEmulator(new BizHawk.Emulation.Common.CoreComm(null, null), core))
|
||||
//a stub corecomm. to reinforce that this won't touch the frontend at all!
|
||||
//LibRetroEmulator should be able to survive having this stub corecomm
|
||||
var coreComm = new BizHawk.Emulation.Common.CoreComm(null, null);
|
||||
using (var retro = new LibRetroEmulator(coreComm, core))
|
||||
{
|
||||
btnLibretroLaunchGame.Enabled = true;
|
||||
if (retro.Description.SupportsNoGame)
|
||||
|
|
|
@ -18,9 +18,15 @@ namespace BizHawk.Emulation.Common
|
|||
string DllPath();
|
||||
|
||||
/// <summary>
|
||||
/// produces a path that contains saveram... because libretro cores need it? not sure yet
|
||||
/// produces a path that contains saveram... because libretro cores need it
|
||||
/// </summary>
|
||||
string GetSaveRAMPath();
|
||||
/// <returns></returns>
|
||||
string GetRetroSaveRAMDirectory();
|
||||
|
||||
/// <summary>
|
||||
/// produces a path for use as a libretro system path (different for each core)
|
||||
/// </summary>
|
||||
string GetRetroSystemPath();
|
||||
|
||||
string GetGameBasePath();
|
||||
|
||||
|
|
|
@ -337,6 +337,89 @@ namespace BizHawk.Emulation.Cores
|
|||
EXPERIMENTAL = 0x10000
|
||||
};
|
||||
|
||||
struct retro_memory_map
|
||||
{
|
||||
public IntPtr descriptors; //retro_memory_descriptor *
|
||||
public uint num_descriptors;
|
||||
};
|
||||
|
||||
struct retro_memory_descriptor
|
||||
{
|
||||
ulong flags;
|
||||
|
||||
/* Pointer to the start of the relevant ROM or RAM chip.
|
||||
* It's strongly recommended to use 'offset' if possible, rather than
|
||||
* doing math on the pointer.
|
||||
*
|
||||
* If the same byte is mapped my multiple descriptors, their descriptors
|
||||
* must have the same pointer.
|
||||
* If 'start' does not point to the first byte in the pointer, put the
|
||||
* difference in 'offset' instead.
|
||||
*
|
||||
* May be NULL if there's nothing usable here (e.g. hardware registers and
|
||||
* open bus). No flags should be set if the pointer is NULL.
|
||||
* It's recommended to minimize the number of descriptors if possible,
|
||||
* but not mandatory. */
|
||||
IntPtr ptr;
|
||||
IntPtr offset; //size_t
|
||||
|
||||
/* This is the location in the emulated address space
|
||||
* where the mapping starts. */
|
||||
IntPtr start; //size_t
|
||||
|
||||
/* Which bits must be same as in 'start' for this mapping to apply.
|
||||
* The first memory descriptor to claim a certain byte is the one
|
||||
* that applies.
|
||||
* A bit which is set in 'start' must also be set in this.
|
||||
* Can be zero, in which case each byte is assumed mapped exactly once.
|
||||
* In this case, 'len' must be a power of two. */
|
||||
IntPtr select; //size_t
|
||||
|
||||
/* If this is nonzero, the set bits are assumed not connected to the
|
||||
* memory chip's address pins. */
|
||||
IntPtr disconnect; //size_t
|
||||
|
||||
/* This one tells the size of the current memory area.
|
||||
* If, after start+disconnect are applied, the address is higher than
|
||||
* this, the highest bit of the address is cleared.
|
||||
*
|
||||
* If the address is still too high, the next highest bit is cleared.
|
||||
* Can be zero, in which case it's assumed to be infinite (as limited
|
||||
* by 'select' and 'disconnect'). */
|
||||
IntPtr len; //size_t
|
||||
|
||||
/* To go from emulated address to physical address, the following
|
||||
* order applies:
|
||||
* Subtract 'start', pick off 'disconnect', apply 'len', add 'offset'.
|
||||
*
|
||||
* The address space name must consist of only a-zA-Z0-9_-,
|
||||
* should be as short as feasible (maximum length is 8 plus the NUL),
|
||||
* and may not be any other address space plus one or more 0-9A-F
|
||||
* at the end.
|
||||
* However, multiple memory descriptors for the same address space is
|
||||
* allowed, and the address space name can be empty. NULL is treated
|
||||
* as empty.
|
||||
*
|
||||
* Address space names are case sensitive, but avoid lowercase if possible.
|
||||
* The same pointer may exist in multiple address spaces.
|
||||
*
|
||||
* Examples:
|
||||
* blank+blank - valid (multiple things may be mapped in the same namespace)
|
||||
* 'Sp'+'Sp' - valid (multiple things may be mapped in the same namespace)
|
||||
* 'A'+'B' - valid (neither is a prefix of each other)
|
||||
* 'S'+blank - valid ('S' is not in 0-9A-F)
|
||||
* 'a'+blank - valid ('a' is not in 0-9A-F)
|
||||
* 'a'+'A' - valid (neither is a prefix of each other)
|
||||
* 'AR'+blank - valid ('R' is not in 0-9A-F)
|
||||
* 'ARB'+blank - valid (the B can't be part of the address either, because
|
||||
* there is no namespace 'AR')
|
||||
* blank+'B' - not valid, because it's ambigous which address space B1234
|
||||
* would refer to.
|
||||
* The length can't be used for that purpose; the frontend may want
|
||||
* to append arbitrary data to an address, without a separator. */
|
||||
string addrspace;
|
||||
};
|
||||
|
||||
public enum RETRO_PIXEL_FORMAT
|
||||
{
|
||||
XRGB1555 = 0,
|
||||
|
|
|
@ -106,6 +106,8 @@ namespace BizHawk.Emulation.Cores
|
|||
case LibRetro.RETRO_ENVIRONMENT.GET_OVERSCAN:
|
||||
return false;
|
||||
case LibRetro.RETRO_ENVIRONMENT.GET_CAN_DUPE:
|
||||
//gambatte requires this
|
||||
*(bool*)data.ToPointer() = true;
|
||||
return true;
|
||||
case LibRetro.RETRO_ENVIRONMENT.SET_MESSAGE:
|
||||
{
|
||||
|
@ -118,14 +120,14 @@ namespace BizHawk.Emulation.Cores
|
|||
case LibRetro.RETRO_ENVIRONMENT.SHUTDOWN:
|
||||
return false;
|
||||
case LibRetro.RETRO_ENVIRONMENT.SET_PERFORMANCE_LEVEL:
|
||||
return false;
|
||||
Console.WriteLine("Core suggested SET_PERFORMANCE_LEVEL {0}", *(uint*)data.ToPointer());
|
||||
return true;
|
||||
case LibRetro.RETRO_ENVIRONMENT.GET_SYSTEM_DIRECTORY:
|
||||
//please write an example of a core that crashes without this (fmsx malfunctions..)
|
||||
//"this is optional, but many cores will silently malfunction without it as they can't load their firmware files"
|
||||
//an alternative (alongside where the saverams and such will go?)
|
||||
//*((IntPtr*)data.ToPointer()) = unmanagedResources.StringToHGlobalAnsi(CoreComm.CoreFileProvider.GetGameBasePath());
|
||||
//mednafen NGP neopop fails to launch with no system directory
|
||||
Directory.CreateDirectory(SystemDirectory); //just to be safe, it seems likely that cores will crash without a created system directory
|
||||
Console.WriteLine("returning system directory: " + SystemDirectory);
|
||||
*((IntPtr*)data.ToPointer()) = SystemDirectoryAtom;
|
||||
return false;
|
||||
return true;
|
||||
case LibRetro.RETRO_ENVIRONMENT.SET_PIXEL_FORMAT:
|
||||
{
|
||||
LibRetro.RETRO_PIXEL_FORMAT fmt = 0;
|
||||
|
@ -222,10 +224,16 @@ namespace BizHawk.Emulation.Cores
|
|||
//supposedly optional like everything else here, but without it ?? crashes (please write which case)
|
||||
//this will suffice for now. if we find evidence later it's needed we can stash a string with
|
||||
//unmanagedResources and CoreFileProvider
|
||||
//*((IntPtr*)data.ToPointer()) = IntPtr.Zero;
|
||||
return false;
|
||||
//mednafen NGP neopop, desmume, and others, request this, and falls back on the system directory if it isn't provided
|
||||
//desmume crashes if the directory doesn't exist
|
||||
Directory.CreateDirectory(SaveDirectory);
|
||||
Console.WriteLine("returning save directory: " + SaveDirectory);
|
||||
*((IntPtr*)data.ToPointer()) = SaveDirectoryAtom;
|
||||
return true;
|
||||
case LibRetro.RETRO_ENVIRONMENT.SET_CONTROLLER_INFO:
|
||||
return true;
|
||||
case LibRetro.RETRO_ENVIRONMENT.SET_MEMORY_MAPS:
|
||||
return false;
|
||||
default:
|
||||
Console.WriteLine("Unknkown retro_environment command {0}", (int)cmd);
|
||||
return false;
|
||||
|
@ -379,16 +387,11 @@ namespace BizHawk.Emulation.Cores
|
|||
public readonly RetroDescription Description = new RetroDescription();
|
||||
|
||||
//path configuration
|
||||
string CoresDirectory;
|
||||
string SystemDirectory;
|
||||
IntPtr SystemDirectoryAtom;
|
||||
string SystemDirectory, SaveDirectory;
|
||||
IntPtr SystemDirectoryAtom, SaveDirectoryAtom;
|
||||
|
||||
public LibRetroEmulator(CoreComm nextComm, string modulename)
|
||||
{
|
||||
CoresDirectory = Path.GetDirectoryName(new FileInfo(modulename).FullName);
|
||||
SystemDirectory = Path.Combine(CoresDirectory, "System");
|
||||
SystemDirectoryAtom = unmanagedResources.StringToHGlobalAnsi(SystemDirectory);
|
||||
|
||||
ServiceProvider = new BasicServiceProvider(this);
|
||||
|
||||
_SyncSettings = new SyncSettings();
|
||||
|
@ -427,8 +430,6 @@ namespace BizHawk.Emulation.Cores
|
|||
|
||||
retro.retro_set_environment(retro_environment_cb);
|
||||
|
||||
retro.retro_init();
|
||||
|
||||
retro.retro_set_video_refresh(retro_video_refresh_cb);
|
||||
retro.retro_set_audio_sample(retro_audio_sample_cb);
|
||||
retro.retro_set_audio_sample_batch(retro_audio_sample_batch_cb);
|
||||
|
@ -442,8 +443,6 @@ namespace BizHawk.Emulation.Cores
|
|||
Description.LibraryVersion = system_info.library_version;
|
||||
Description.ValidExtensions = system_info.valid_extensions;
|
||||
Description.SupportsNoGame = environmentInfo.SupportNoGame;
|
||||
//variables need to be done ahead of time, when theyre set through the environment
|
||||
//some retro_init (for example, desmume) will continue to use variables (and maybe other parts of the environment) from within retro_init
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
@ -487,6 +486,25 @@ namespace BizHawk.Emulation.Cores
|
|||
|
||||
bool LoadWork(ref LibRetro.retro_game_info gi)
|
||||
{
|
||||
//defer this until loading because during the LibRetroEmulator constructor, we dont have access to the game name and so paths can't be selected
|
||||
//this cannot be done until set_environment is complete
|
||||
if (CoreComm.CoreFileProvider == null)
|
||||
{
|
||||
SaveDirectory = SystemDirectory = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
SystemDirectory = CoreComm.CoreFileProvider.GetRetroSystemPath();
|
||||
SaveDirectory = CoreComm.CoreFileProvider.GetRetroSaveRAMDirectory();
|
||||
SystemDirectoryAtom = unmanagedResources.StringToHGlobalAnsi(SystemDirectory);
|
||||
SaveDirectoryAtom = unmanagedResources.StringToHGlobalAnsi(SaveDirectory);
|
||||
}
|
||||
|
||||
//defer this until loading because it triggers the core to read save and system paths
|
||||
//if any cores did that from set_environment then i'm assured we can call set_environment again here before retro_init and it should work
|
||||
//--alcaro says any cores that can't handle that should be considered a bug
|
||||
retro.retro_init();
|
||||
|
||||
if (!retro.retro_load_game(ref gi))
|
||||
{
|
||||
Console.WriteLine("retro_load_game() failed");
|
||||
|
@ -605,7 +623,7 @@ namespace BizHawk.Emulation.Cores
|
|||
[FeatureNotImplemented]
|
||||
get
|
||||
{
|
||||
//if we dont have saveram, it isnt modified. otherwise, assume iti s
|
||||
//if we dont have saveram, it isnt modified. otherwise, assume it is
|
||||
int size = (int)retro.retro_get_memory_size(LibRetro.RETRO_MEMORY.SAVE_RAM);
|
||||
if (size == 0)
|
||||
return false;
|
||||
|
@ -877,7 +895,7 @@ namespace BizHawk.Emulation.Cores
|
|||
{
|
||||
get
|
||||
{
|
||||
if(dar==0)
|
||||
if(dar<=0)
|
||||
return BufferWidth;
|
||||
else if (dar > 1.0f)
|
||||
return (int)(BufferHeight * dar);
|
||||
|
@ -889,7 +907,7 @@ namespace BizHawk.Emulation.Cores
|
|||
{
|
||||
get
|
||||
{
|
||||
if(dar==0)
|
||||
if(dar<=0)
|
||||
return BufferHeight;
|
||||
if (dar < 1.0f)
|
||||
return (int)(BufferWidth / dar);
|
||||
|
|
Loading…
Reference in New Issue