public static bool LoadStateFile(string path, string name) { var core = Global.Emulator.AsStatable(); // try to detect binary first var bl = BinaryStateLoader.LoadAndDetect(path); if (bl != null) { try { var succeed = false; if (Global.MovieSession.Movie.IsActive()) { bl.GetLump(BinaryStateLump.Input, true, tr => succeed = Global.MovieSession.HandleMovieLoadState_HackyStep1(tr)); if (!succeed) { return(false); } bl.GetLump(BinaryStateLump.Input, true, tr => succeed = Global.MovieSession.HandleMovieLoadState_HackyStep2(tr)); if (!succeed) { return(false); } } using (new SimpleTime("Load Core")) { bl.GetCoreState(br => core.LoadStateBinary(br), tr => core.LoadStateText(tr)); } bl.GetLump(BinaryStateLump.Framebuffer, false, PopulateFramebuffer); string userData = ""; bl.GetLump(BinaryStateLump.UserData, false, delegate(TextReader tr) { string line; while ((line = tr.ReadLine()) != null) { if (!string.IsNullOrWhiteSpace(line)) { userData = line; } } }); if (!string.IsNullOrWhiteSpace(userData)) { Global.UserBag = (Dictionary <string, object>)ConfigService.LoadWithType(userData); } if (Global.MovieSession.Movie.IsActive() && Global.MovieSession.Movie is TasMovie) { bl.GetLump(BinaryStateLump.LagLog, false, delegate(TextReader tr) { ((TasMovie)Global.MovieSession.Movie).TasLagLog.Load(tr); }); } } finally { bl.Dispose(); } return(true); } return(false); }
protected override void RunImport() { var neshawkName = ((CoreAttribute)Attribute.GetCustomAttribute(typeof(NES), typeof(CoreAttribute))).CoreName; Result.Movie.HeaderEntries[HeaderKeys.Core] = neshawkName; const string emulator = "FCEUX"; var platform = "NES"; // TODO: FDS? var syncSettings = new NES.NESSyncSettings(); var controllerSettings = new NESControlSettings { NesLeftPort = nameof(UnpluggedNES), NesRightPort = nameof(UnpluggedNES) }; _deck = controllerSettings.Instantiate((x, y) => true); AddDeckControlButtons(); Result.Movie.HeaderEntries[HeaderKeys.Platform] = platform; using var sr = SourceFile.OpenText(); string line; while ((line = sr.ReadLine()) != null) { if (line == "") { continue; } if (line[0] == '|') { ImportInputFrame(line); } else if (line.ToLower().StartsWith("sub")) { var subtitle = ImportTextSubtitle(line); if (!string.IsNullOrEmpty(subtitle)) { Result.Movie.Subtitles.AddFromString(subtitle); } } else if (line.ToLower().StartsWith("emuversion")) { Result.Movie.Comments.Add($"{EmulationOrigin} {emulator} version {ParseHeader(line, "emuVersion")}"); } else if (line.ToLower().StartsWith("version")) { string version = ParseHeader(line, "version"); if (version != "3") { Result.Warnings.Add("Detected a .fm2 movie version other than 3, which is unsupported"); } else { Result.Movie.Comments.Add($"{MovieOrigin} .fm2 version 3"); } } else if (line.ToLower().StartsWith("romfilename")) { Result.Movie.HeaderEntries[HeaderKeys.GameName] = ParseHeader(line, "romFilename"); } else if (line.ToLower().StartsWith("cdgamename")) { Result.Movie.HeaderEntries[HeaderKeys.GameName] = ParseHeader(line, "cdGameName"); } else if (line.ToLower().StartsWith("romchecksum")) { string blob = ParseHeader(line, "romChecksum"); byte[] md5 = DecodeBlob(blob); if (md5 != null && md5.Length == 16) { Result.Movie.HeaderEntries[MD5] = md5.BytesToHexString().ToLower(); } else { Result.Warnings.Add("Bad ROM checksum."); } } else if (line.ToLower().StartsWith("comment author")) { Result.Movie.HeaderEntries[HeaderKeys.Author] = ParseHeader(line, "comment author"); } else if (line.ToLower().StartsWith("rerecordcount")) { int.TryParse(ParseHeader(line, "rerecordCount"), out var rerecordCount); Result.Movie.Rerecords = (ulong)rerecordCount; } else if (line.ToLower().StartsWith("guid")) { // We no longer care to keep this info } else if (line.ToLower().StartsWith("startsfromsavestate")) { // If this movie starts from a savestate, we can't support it. if (ParseHeader(line, "StartsFromSavestate") == "1") { Result.Errors.Add("Movies that begin with a savestate are not supported."); break; } } else if (line.ToLower().StartsWith("palflag")) { Result.Movie.HeaderEntries[HeaderKeys.Pal] = ParseHeader(line, "palFlag"); } else if (line.ToLower().StartsWith("port0")) { if (ParseHeader(line, "port0") == "1") { controllerSettings.NesLeftPort = nameof(ControllerNES); _deck = controllerSettings.Instantiate((x, y) => false); AddDeckControlButtons(); } } else if (line.ToLower().StartsWith("port1")) { if (ParseHeader(line, "port1") == "1") { controllerSettings.NesRightPort = nameof(ControllerNES); _deck = controllerSettings.Instantiate((x, y) => false); AddDeckControlButtons(); } } else if (line.ToLower().StartsWith("port2")) { if (ParseHeader(line, "port2") == "1") { Result.Errors.Add("Famicom port not yet supported"); break; } } else if (line.ToLower().StartsWith("fourscore")) { bool fourscore = ParseHeader(line, "fourscore") == "1"; if (fourscore) { // TODO: set controller config sync settings controllerSettings.NesLeftPort = nameof(FourScore); controllerSettings.NesRightPort = nameof(FourScore); } _deck = controllerSettings.Instantiate((x, y) => false); } else { Result.Movie.Comments.Add(line); // Everything not explicitly defined is treated as a comment. } } syncSettings.Controls = controllerSettings; Result.Movie.SyncSettingsJson = ConfigService.SaveWithType(syncSettings); }
// TODO: This doesn't really belong here, but not sure where to put it public static void PopulateWithDefaultHeaderValues( this IMovie movie, IEmulator emulator, IGameInfo game, FirmwareManager firmwareManager, string author) { movie.Author = author; movie.EmulatorVersion = VersionInfo.GetEmuVersion(); movie.OriginalEmulatorVersion = VersionInfo.GetEmuVersion(); movie.SystemID = emulator.SystemId; var settable = new SettingsAdapter(emulator); if (settable.HasSyncSettings) { movie.SyncSettingsJson = ConfigService.SaveWithType(settable.GetSyncSettings()); } if (game.IsNullInstance()) { movie.GameName = "NULL"; } else { movie.GameName = game.FilesystemSafeName(); movie.Hash = game.Hash; if (game.FirmwareHash != null) { movie.FirmwareHash = game.FirmwareHash; } } if (emulator.HasBoardInfo()) { movie.BoardName = emulator.AsBoardInfo().BoardName; } if (emulator.HasRegions()) { var region = emulator.AsRegionable().Region; if (region == Emulation.Common.DisplayType.PAL) { movie.HeaderEntries.Add(HeaderKeys.Pal, "1"); } } if (firmwareManager.RecentlyServed.Any()) { foreach (var firmware in firmwareManager.RecentlyServed) { var key = $"{firmware.SystemId}_Firmware_{firmware.FirmwareId}"; if (!movie.HeaderEntries.ContainsKey(key)) { movie.HeaderEntries.Add(key, firmware.Hash); } } } if (emulator is GBHawk gbHawk && gbHawk.IsCGBMode()) { movie.HeaderEntries.Add("IsCGBMode", "1"); } if (emulator is SubGBHawk subgbHawk) { if (subgbHawk._GBCore.IsCGBMode()) { movie.HeaderEntries.Add("IsCGBMode", "1"); } movie.HeaderEntries.Add(HeaderKeys.CycleCount, "0"); } if (emulator is Gameboy gb) { if (gb.IsCGBMode()) { movie.HeaderEntries.Add("IsCGBMode", "1"); } movie.HeaderEntries.Add(HeaderKeys.CycleCount, "0"); } if (emulator is SMS sms) { if (sms.IsSG1000) { movie.HeaderEntries.Add("IsSGMode", "1"); } if (sms.IsGameGear) { movie.HeaderEntries.Add("IsGGMode", "1"); } } if (emulator is GPGX gpgx && gpgx.IsMegaCD) { movie.HeaderEntries.Add("IsSegaCDMode", "1"); } if (emulator is PicoDrive pico && pico.Is32XActive) { movie.HeaderEntries.Add("Is32X", "1"); } if (emulator is SubNESHawk) { movie.HeaderEntries.Add(HeaderKeys.VBlankCount, "0"); } movie.Core = ((CoreAttribute)Attribute .GetCustomAttribute(emulator.GetType(), typeof(CoreAttribute))) .CoreName; }
public static void SaveStateFile(string filename, string name) { var core = Global.Emulator.AsStatable(); // the old method of text savestate save is now gone. // a text savestate is just like a binary savestate, but with a different core lump using var bs = new BinaryStateSaver(filename); if (Global.Config.SaveStateType == SaveStateTypeE.Text || (Global.Config.SaveStateType == SaveStateTypeE.Default && !core.BinarySaveStatesPreferred)) { // text savestate format using (new SimpleTime("Save Core")) { bs.PutLump(BinaryStateLump.CorestateText, tw => core.SaveStateText(tw)); } } else { // binary core lump format using (new SimpleTime("Save Core")) { bs.PutLump(BinaryStateLump.Corestate, bw => core.SaveStateBinary(bw)); } } if (Global.Config.SaveScreenshotWithStates && Global.Emulator.HasVideoProvider()) { var vp = Global.Emulator.AsVideoProvider(); var buff = vp.GetVideoBuffer(); if (buff.Length == 1) { // is a hacky opengl texture ID. can't handle this now! // need to discuss options // 1. cores must be able to provide a pixels VideoProvider in addition to a texture ID, on command (not very hard overall but interface changing and work per core) // 2. SavestateManager must be setup with a mechanism for resolving texture IDs (even less work, but sloppy) // There are additional problems with AVWriting. They depend on VideoProvider providing pixels. } else { int outWidth = vp.BufferWidth; int outHeight = vp.BufferHeight; // if buffer is too big, scale down screenshot if (!Global.Config.NoLowResLargeScreenshotWithStates && buff.Length >= Global.Config.BigScreenshotSize) { outWidth /= 2; outHeight /= 2; } using (new SimpleTime("Save Framebuffer")) { bs.PutLump(BinaryStateLump.Framebuffer, s => QuickBmpFile.Save(Global.Emulator.AsVideoProvider(), s, outWidth, outHeight)); } } } if (Global.MovieSession.Movie.IsActive()) { bs.PutLump(BinaryStateLump.Input, delegate(TextWriter tw) { // this never should have been a core's responsibility tw.WriteLine("Frame {0}", Global.Emulator.Frame); Global.MovieSession.HandleMovieSaveState(tw); }); } if (Global.UserBag.Any()) { bs.PutLump(BinaryStateLump.UserData, delegate(TextWriter tw) { var data = ConfigService.SaveWithType(Global.UserBag); tw.WriteLine(data); }); } if (Global.MovieSession.Movie.IsActive() && Global.MovieSession.Movie is TasMovie) { bs.PutLump(BinaryStateLump.LagLog, delegate(TextWriter tw) { ((TasMovie)Global.MovieSession.Movie).TasLagLog.Save(tw); }); } }
// TODO: This doesn't really belong here, but not sure where to put it public static void PopulateWithDefaultHeaderValues( this IMovie movie, IEmulator emulator, ISettingsAdapter settable, IGameInfo game, FirmwareManager firmwareManager, string author) { movie.Author = author; movie.EmulatorVersion = VersionInfo.GetEmuVersion(); movie.OriginalEmulatorVersion = VersionInfo.GetEmuVersion(); movie.SystemID = emulator.SystemId; if (settable.HasSyncSettings) { movie.SyncSettingsJson = ConfigService.SaveWithType(settable.GetSyncSettings()); } if (game.IsNullInstance()) { movie.GameName = "NULL"; } else { movie.GameName = game.FilesystemSafeName(); movie.Hash = game.Hash; if (game.FirmwareHash != null) { movie.FirmwareHash = game.FirmwareHash; } } if (emulator.HasBoardInfo()) { movie.BoardName = emulator.AsBoardInfo().BoardName; } if (emulator.HasRegions()) { var region = emulator.AsRegionable().Region; if (region == DisplayType.PAL) { movie.HeaderEntries.Add(HeaderKeys.Pal, "1"); } } if (firmwareManager.RecentlyServed.Count != 0) { foreach (var firmware in firmwareManager.RecentlyServed) { var key = firmware.ID.MovieHeaderKey; if (!movie.HeaderEntries.ContainsKey(key)) { movie.HeaderEntries.Add(key, firmware.Hash); } } } if (emulator is NDS nds && nds.IsDSi) { movie.HeaderEntries.Add("IsDSi", "1"); if (nds.IsDSiWare) { movie.HeaderEntries.Add("IsDSiWare", "1"); } } if ((emulator is NES nes && nes.IsVS) || (emulator is SubNESHawk subnes && subnes.IsVs)) { movie.HeaderEntries.Add("IsVS", "1"); } if (emulator is IGameboyCommon gb) { if (gb.IsCGBMode()) { movie.HeaderEntries.Add(gb.IsCGBDMGMode() ? "IsCGBDMGMode" : "IsCGBMode", "1"); } } if (emulator is SMS sms) { if (sms.IsSG1000) { movie.HeaderEntries.Add("IsSGMode", "1"); } if (sms.IsGameGear) { movie.HeaderEntries.Add("IsGGMode", "1"); } } if (emulator is GPGX gpgx && gpgx.IsMegaCD) { movie.HeaderEntries.Add("IsSegaCDMode", "1"); } if (emulator is PicoDrive pico && pico.Is32XActive) { movie.HeaderEntries.Add("Is32X", "1"); } if (emulator is ICycleTiming) { movie.HeaderEntries.Add(HeaderKeys.CycleCount, "0"); movie.HeaderEntries.Add(HeaderKeys.ClockRate, "0"); } movie.Core = emulator.Attributes().CoreName; }
protected override void RunImport() { Result.Movie.HeaderEntries[HeaderKeys.Platform] = "NDS"; var syncSettings = new MelonDS.MelonSyncSettings(); using var sr = SourceFile.OpenText(); string line; while ((line = sr.ReadLine()) != null) { if (string.IsNullOrWhiteSpace(line)) { continue; } if (line[0] == '|') { ImportInputFrame(line); } else if (line.StartsWith("rerecordCount")) { int.TryParse(ParseHeader(line, "rerecordCount"), out var rerecordCount); Result.Movie.Rerecords = (ulong)rerecordCount; } else if (line.StartsWith("firmNickname")) { syncSettings.Nickname = ParseHeader(line, "firmNickname"); } else if (line.StartsWith("firmFavColour")) { syncSettings.FavoriteColor = byte.Parse(ParseHeader(line, "firmFavColour")); } else if (line.StartsWith("firmBirthDay")) { syncSettings.BirthdayDay = byte.Parse(ParseHeader(line, "firmBirthDay")); } else if (line.StartsWith("firmBirthMonth")) { syncSettings.BirthdayMonth = byte.Parse(ParseHeader(line, "firmBirthMonth")); } else if (line.StartsWith("rtcStartNew")) { //TODO: what is this format?? 2010-JAN-01 00:00:00:000 //var time = DateTime.Parse(ParseHeader(line, "rtcStartNew")); //syncSettings.TimeAtBoot = (uint)new DateTimeOffset(time.ToLocalTime()).ToUnixTimeSeconds(); } else if (line.StartsWith("comment author")) { Result.Movie.HeaderEntries[HeaderKeys.Author] = ParseHeader(line, "comment author"); } else if (line.StartsWith("comment")) { Result.Movie.Comments.Add(ParseHeader(line, "comment")); } else if (line.ToLower().StartsWith("guid")) { // We no longer care to keep this info } else { Result.Movie.Comments.Add(line); // Everything not explicitly defined is treated as a comment. } Result.Movie.SyncSettingsJson = ConfigService.SaveWithType(syncSettings); } }
public void Create(string filename, SaveStateConfig config) { // the old method of text savestate save is now gone. // a text savestate is just like a binary savestate, but with a different core lump using var bs = new ZipStateSaver(filename, config.CompressionLevelNormal); bs.PutVersionLumps(); using (new SimpleTime("Save Core")) { if (config.Type == SaveStateType.Text) { bs.PutLump(BinaryStateLump.CorestateText, tw => _statable.SaveStateText(tw)); } else { bs.PutLump(BinaryStateLump.Corestate, bw => _statable.SaveStateBinary(bw)); } } if (config.SaveScreenshot && _videoProvider != null) { var buff = _videoProvider.GetVideoBuffer(); if (buff.Length == 1) { // is a hacky opengl texture ID. can't handle this now! // need to discuss options // 1. cores must be able to provide a pixels VideoProvider in addition to a texture ID, on command (not very hard overall but interface changing and work per core) // 2. SavestateManager must be setup with a mechanism for resolving texture IDs (even less work, but sloppy) // There are additional problems with AVWriting. They depend on VideoProvider providing pixels. } else { int outWidth = _videoProvider.BufferWidth; int outHeight = _videoProvider.BufferHeight; // if buffer is too big, scale down screenshot if (!config.NoLowResLargeScreenshots && buff.Length >= config.BigScreenshotSize) { outWidth /= 2; outHeight /= 2; } using (new SimpleTime("Save Framebuffer")) { bs.PutLump(BinaryStateLump.Framebuffer, s => _quickBmpFile.Save(_videoProvider, s, outWidth, outHeight)); } } } if (_movieSession.Movie.IsActive()) { bs.PutLump(BinaryStateLump.Input, delegate(TextWriter tw) { // this never should have been a core's responsibility tw.WriteLine("Frame {0}", _emulator.Frame); _movieSession.HandleSaveState(tw); }); } if (_userBag.Any()) { bs.PutLump(BinaryStateLump.UserData, delegate(TextWriter tw) { var data = ConfigService.SaveWithType(_userBag); tw.WriteLine(data); }); } if (_movieSession.Movie.IsActive() && _movieSession.Movie is ITasMovie) { bs.PutLump(BinaryStateLump.LagLog, delegate(TextWriter tw) { ((ITasMovie)_movieSession.Movie).LagLog.Save(tw); }); } }
public static bool LoadStateFile(string path, string name) { var core = Global.Emulator.AsStatable(); // try to detect binary first var bl = BinaryStateLoader.LoadAndDetect(path); if (bl != null) { try { var succeed = false; if (Global.MovieSession.Movie.IsActive) { bl.GetLump(BinaryStateLump.Input, true, tr => succeed = Global.MovieSession.HandleMovieLoadState_HackyStep1(tr)); if (!succeed) { return(false); } bl.GetLump(BinaryStateLump.Input, true, tr => succeed = Global.MovieSession.HandleMovieLoadState_HackyStep2(tr)); if (!succeed) { return(false); } } using (new SimpleTime("Load Core")) { bl.GetCoreState(br => core.LoadStateBinary(br), tr => core.LoadStateText(tr)); } bl.GetLump(BinaryStateLump.Framebuffer, false, PopulateFramebuffer); if (bl.HasLump(BinaryStateLump.UserData)) { string userData = string.Empty; bl.GetLump(BinaryStateLump.UserData, false, delegate(TextReader tr) { string line; while ((line = tr.ReadLine()) != null) { if (!string.IsNullOrWhiteSpace(line)) { userData = line; } } }); Global.UserBag = (Dictionary <string, object>)ConfigService.LoadWithType(userData); } if (bl.HasLump(BinaryStateLump.LagLog) && Global.MovieSession.Movie.IsActive && Global.MovieSession.Movie is TasMovie) { bl.GetLump(BinaryStateLump.LagLog, false, delegate(BinaryReader br, long length) { (Global.MovieSession.Movie as TasMovie).TasLagLog.Load(br); }); } } catch { return(false); } finally { bl.Dispose(); } return(true); } else // text mode { if (Global.MovieSession.HandleMovieLoadState(path)) { using (var reader = new StreamReader(path)) { core.LoadStateText(reader); while (true) { var str = reader.ReadLine(); if (str == null) { break; } if (str.Trim() == string.Empty) { continue; } var args = str.Split(' '); if (args[0] == "Framebuffer" && Global.Emulator.HasVideoProvider()) { Global.Emulator.AsVideoProvider().GetVideoBuffer().ReadFromHex(args[1]); } } } return(true); } else { return(false); } } }
public bool Load(string path) { // try to detect binary first var bl = ZipStateLoader.LoadAndDetect(path); if (bl != null) { try { var succeed = false; // Movie timeline check must happen before the core state is loaded if (_movieSession.Movie.IsActive()) { bl.GetLump(BinaryStateLump.Input, true, tr => succeed = _movieSession.CheckSavestateTimeline(tr)); if (!succeed) { return(false); } } using (new SimpleTime("Load Core")) { bl.GetCoreState(br => _statable.LoadStateBinary(br), tr => _statable.LoadStateText(tr)); } // We must handle movie input AFTER the core is loaded to properly handle mode changes, and input latching if (_movieSession.Movie.IsActive()) { bl.GetLump(BinaryStateLump.Input, true, tr => succeed = _movieSession.HandleLoadState(tr)); if (!succeed) { return(false); } } if (_videoProvider != null) { bl.GetLump(BinaryStateLump.Framebuffer, false, br => PopulateFramebuffer(br, _videoProvider, _quickBmpFile)); } string userData = ""; bl.GetLump(BinaryStateLump.UserData, false, delegate(TextReader tr) { string line; while ((line = tr.ReadLine()) != null) { if (!string.IsNullOrWhiteSpace(line)) { userData = line; } } }); if (!string.IsNullOrWhiteSpace(userData)) { var bag = (Dictionary <string, object>)ConfigService.LoadWithType(userData); _userBag.Clear(); foreach (var(k, v) in bag) { _userBag.Add(k, v); } } if (_movieSession.Movie.IsActive() && _movieSession.Movie is ITasMovie) { bl.GetLump(BinaryStateLump.LagLog, false, delegate(TextReader tr) { ((ITasMovie)_movieSession.Movie).LagLog.Load(tr); }); } } finally { bl.Dispose(); } return(true); } return(false); }
public static bool LoadStateFile(IEmulator emulator, string path) { var core = emulator.AsStatable(); // try to detect binary first var bl = BinaryStateLoader.LoadAndDetect(path); if (bl != null) { try { var succeed = false; // Movie timeline check must happen before the core state is loaded if (Global.MovieSession.Movie.IsActive()) { bl.GetLump(BinaryStateLump.Input, true, tr => succeed = Global.MovieSession.CheckSavestateTimeline(tr)); if (!succeed) { return(false); } } using (new SimpleTime("Load Core")) { bl.GetCoreState(br => core.LoadStateBinary(br), tr => core.LoadStateText(tr)); } // We must handle movie input AFTER the core is loaded to properly handle mode changes, and input latching if (Global.MovieSession.Movie.IsActive()) { bl.GetLump(BinaryStateLump.Input, true, tr => succeed = Global.MovieSession.HandleLoadState(tr)); if (!succeed) { return(false); } } bl.GetLump(BinaryStateLump.Framebuffer, false, PopulateFramebuffer); string userData = ""; bl.GetLump(BinaryStateLump.UserData, false, delegate(TextReader tr) { string line; while ((line = tr.ReadLine()) != null) { if (!string.IsNullOrWhiteSpace(line)) { userData = line; } } }); if (!string.IsNullOrWhiteSpace(userData)) { Global.UserBag = (Dictionary <string, object>)ConfigService.LoadWithType(userData); } if (Global.MovieSession.Movie.IsActive() && Global.MovieSession.Movie is TasMovie) { bl.GetLump(BinaryStateLump.LagLog, false, delegate(TextReader tr) { ((TasMovie)Global.MovieSession.Movie).LagLog.Load(tr); }); } } finally { bl.Dispose(); } return(true); } return(false); }
protected MiscHeaderInfo ParseHeader(IMovie movie, string expectedMagic, BinaryReader br) { var info = new MiscHeaderInfo(); string magic = new string(br.ReadChars(4)); if (magic != expectedMagic) { Result.Errors.Add($"Not a {expectedMagic}file: invalid magic number in file header."); return(info); } uint movieVersionNumber = br.ReadUInt32(); if (movieVersionNumber != 2) { Result.Warnings.Add($"Unexpected movie version: got {movieVersionNumber}, expecting 2"); } // 008: UInt32 emulator version. br.ReadUInt32(); byte flags = br.ReadByte(); byte flags2 = br.ReadByte(); if ((flags & 0x02) != 0) { Result.Errors.Add("Movie starts from savestate; this is currently unsupported."); } if ((flags & 0x04) != 0) { movie.HeaderEntries[HeaderKeys.Pal] = "1"; } if ((flags & 0x08) != 0) { Result.Errors.Add("Movie contains embedded memory cards; this is currently unsupported."); } if ((flags & 0x10) != 0) { Result.Errors.Add("Movie contains embedded cheat list; this is currently unsupported."); } if ((flags & 0x20) != 0 || (flags2 & 0x06) != 0) { Result.Errors.Add("Movie relies on emulator hacks; this is currently unsupported."); } if ((flags & 0x40) != 0) { info.BinaryFormat = false; } if ((flags & 0x80) != 0 || (flags2 & 0x01) != 0) { Result.Errors.Add("Movie uses multitap; this is currently unsupported."); return(info); } // Player 1 controller type switch (br.ReadByte()) { // It seems to be inconsistent in the files I looked at which of these is used // to mean no controller present. case 0: case 8: info.Player1Type = OctoshockDll.ePeripheralType.None; break; case 4: info.Player1Type = OctoshockDll.ePeripheralType.Pad; break; case 7: info.Player1Type = OctoshockDll.ePeripheralType.DualShock; break; default: Result.Errors.Add("Movie has unrecognized controller type for Player 1."); return(info); } // Player 2 controller type switch (br.ReadByte()) { case 0: case 8: info.Player2Type = OctoshockDll.ePeripheralType.None; break; case 4: info.Player2Type = OctoshockDll.ePeripheralType.Pad; break; case 7: info.Player2Type = OctoshockDll.ePeripheralType.DualShock; break; default: Result.Errors.Add("Movie has unrecognized controller type for Player 2."); return(info); } var syncSettings = new Octoshock.SyncSettings { FIOConfig = { Devices8 = new[] { info.Player1Type, OctoshockDll.ePeripheralType.None, OctoshockDll.ePeripheralType.None, OctoshockDll.ePeripheralType.None, info.Player2Type, OctoshockDll.ePeripheralType.None, OctoshockDll.ePeripheralType.None, OctoshockDll.ePeripheralType.None } } }; movie.SyncSettingsJson = ConfigService.SaveWithType(syncSettings); info.FrameCount = br.ReadUInt32(); uint rerecordCount = br.ReadUInt32(); movie.HeaderEntries[HeaderKeys.Rerecords] = rerecordCount.ToString(); // 018: UInt32 savestateOffset // 01C: UInt32 memoryCard1Offset // 020: UInt32 memoryCard2Offset // 024: UInt32 cheatListOffset // 028: UInt32 cdRomIdOffset // Source format is just the first up-to-8 alphanumeric characters of the CD label, // so not so useful. br.ReadBytes(20); info.ControllerDataOffset = br.ReadUInt32(); uint authorNameLength = br.ReadUInt32(); char[] authorName = br.ReadChars((int)authorNameLength); movie.HeaderEntries[HeaderKeys.Author] = new string(authorName); info.ParseSuccessful = true; return(info); }
public static void SaveStateFile(string filename, string name) { var core = Global.Emulator.AsStatable(); // the old method of text savestate save is now gone. // a text savestate is just like a binary savestate, but with a different core lump using (var bs = new BinaryStateSaver(filename)) { if (Global.Config.SaveStateType == Config.SaveStateTypeE.Text || (Global.Config.SaveStateType == Config.SaveStateTypeE.Default && !core.BinarySaveStatesPreferred)) { // text savestate format using (new SimpleTime("Save Core")) bs.PutLump(BinaryStateLump.CorestateText, (tw) => core.SaveStateText(tw)); } else { // binary core lump format using (new SimpleTime("Save Core")) bs.PutLump(BinaryStateLump.Corestate, bw => core.SaveStateBinary(bw)); } if (Global.Config.SaveScreenshotWithStates) { var vp = Global.Emulator.VideoProvider(); var buff = vp.GetVideoBuffer(); int out_w = vp.BufferWidth; int out_h = vp.BufferHeight; // if buffer is too big, scale down screenshot if (!Global.Config.NoLowResLargeScreenshotWithStates && buff.Length >= Global.Config.BigScreenshotSize) { out_w /= 2; out_h /= 2; } using (new SimpleTime("Save Framebuffer")) bs.PutLump(BinaryStateLump.Framebuffer, (s) => QuickBmpFile.Save(Global.Emulator.VideoProvider(), s, out_w, out_h)); } if (Global.MovieSession.Movie.IsActive) { bs.PutLump(BinaryStateLump.Input, delegate(TextWriter tw) { // this never should have been a core's responsibility tw.WriteLine("Frame {0}", Global.Emulator.Frame); Global.MovieSession.HandleMovieSaveState(tw); }); } if (Global.UserBag.Any()) { bs.PutLump(BinaryStateLump.UserData, delegate(TextWriter tw) { var data = ConfigService.SaveWithType(Global.UserBag); tw.WriteLine(data); }); } if (Global.MovieSession.Movie.IsActive && Global.MovieSession.Movie is TasMovie) { bs.PutLump(BinaryStateLump.LagLog, delegate(BinaryWriter bw) { (Global.MovieSession.Movie as TasMovie).TasLagLog.Save(bw); }); } } }
public bool Load(string path, IDialogParent dialogParent) { // try to detect binary first using var bl = ZipStateLoader.LoadAndDetect(path); if (bl is null) { return(false); } var succeed = false; if (!VersionInfo.DeveloperBuild) { bl.GetLump(BinaryStateLump.BizVersion, true, tr => succeed = tr.ReadLine() == VersionInfo.GetEmuVersion()); if (!succeed) { var result = dialogParent.ModalMessageBox2( "This savestate was made with a different version, so it's unlikely to work.\nChoose OK to try loading it anyway.", "Savestate version mismatch", EMsgBoxIcon.Question, useOKCancel: true); if (!result) { return(false); } } } // Movie timeline check must happen before the core state is loaded if (_movieSession.Movie.IsActive()) { bl.GetLump(BinaryStateLump.Input, true, tr => succeed = _movieSession.CheckSavestateTimeline(tr)); if (!succeed) { return(false); } } using (new SimpleTime("Load Core")) { bl.GetCoreState(br => _statable.LoadStateBinary(br), tr => _statable.LoadStateText(tr)); } // We must handle movie input AFTER the core is loaded to properly handle mode changes, and input latching if (_movieSession.Movie.IsActive()) { bl.GetLump(BinaryStateLump.Input, true, tr => succeed = _movieSession.HandleLoadState(tr)); if (!succeed) { return(false); } } if (_videoProvider != null) { bl.GetLump(BinaryStateLump.Framebuffer, false, br => PopulateFramebuffer(br, _videoProvider, _quickBmpFile)); } string userData = ""; bl.GetLump(BinaryStateLump.UserData, false, delegate(TextReader tr) { string line; while ((line = tr.ReadLine()) != null) { if (!string.IsNullOrWhiteSpace(line)) { userData = line; } } }); if (!string.IsNullOrWhiteSpace(userData)) { var bag = (Dictionary <string, object>)ConfigService.LoadWithType(userData); _userBag.Clear(); foreach (var(k, v) in bag) { _userBag.Add(k, v); } } if (_movieSession.Movie.IsActive() && _movieSession.Movie is ITasMovie) { bl.GetLump(BinaryStateLump.LagLog, false, delegate(TextReader tr) { ((ITasMovie)_movieSession.Movie).LagLog.Load(tr); }); } return(true); }